home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 008a / pandor23.zip / PANDORA.ASM < prev    next >
Assembly Source File  |  1992-01-03  |  76KB  |  2,922 lines

  1.     title    'Pandora:  Program ANimator by Pete Maclean'
  2.  
  3.  
  4. ;NOTE: This source code is for Version 1.5a.  However, there is
  5. ;a later version of executable available which is 2.3.
  6.  
  7.  
  8. ;;;    include    pandora.hdr
  9.  
  10. ; Symbol definitions
  11.  
  12. CR        =    13    ; ASCII carriage return
  13. LF        =    10    ; ASCII linefeed
  14. TAB        =    9    ; ASCII Tab
  15.  
  16. ; BIOS Keyboard Buffer definitions
  17.  
  18. KBB_SEGADD    =    40h    ; segment address of buffer
  19. KBB_HEAD    =    1Ah    ; offset to head pointer
  20. KBB_TAIL    =    1Ch    ; offset to tail pointer
  21. KBB_START    =    80h    ; offset to start pointer
  22. KBB_END        =    82h    ; offset to end pointer
  23.  
  24. ; Pandora States
  25.  
  26. PS_INITIAL    =    0    ; initial state - no target program loaded
  27. PS_LOADED    =    1    ; target program loaded
  28. PS_RUNNING    =    2    ; target program running
  29. PS_OBIT        =    3    ; waiting for target program to die
  30. PS_QUIT        =    4    ; QUIT pending when target program dies
  31.  
  32. code    segment    para public 'code'
  33.     assume    cs:code, ds:code
  34.     org    100h
  35. start:    jmp    main        ; entry point
  36.  
  37. ; Messages
  38.  
  39. initmsg        db    'Pandora 1.5a (c) 1990 Ziff Communications Co.',CR,LF
  40.         db    'PC Magazine ',254,' Pete Maclean',CR,LF,'$'
  41.  
  42. crlfz        db    CR,LF,0
  43.  
  44. ; Definition for command-table entry
  45.  
  46. COMMAND        STRUC
  47. PC_KEY        dw    ?    ; (offset) address of command key
  48. PC_PROC        dw    ?    ; (offset) address of command processor
  49. PC_TYPE        db    ?    ; coded command type
  50. COMMAND        ENDS
  51.  
  52. command_entry_size    db    SIZE COMMAND
  53.  
  54. ; Command types
  55.  
  56. PCT_REG        =    0    ; regular command
  57. PCT_IF        =    2    ; If command
  58. PCT_ELSE    =    4    ; Else command
  59. PCT_FI        =    6    ; EndIf command
  60.  
  61. ; Command table
  62.  
  63. command_table    LABEL    COMMAND
  64.         COMMAND    <k_Alt,        c_Alt,        PCT_REG>
  65.         COMMAND    <k_Break,    c_Break,    PCT_REG>
  66.         COMMAND    <k_CapsLock,    c_CapsLock,    PCT_REG>
  67.         COMMAND    <k_Ctrl,    c_Ctrl,        PCT_REG>
  68.         COMMAND    <k_Cursor,    c_Cursor,    PCT_REG>
  69.         COMMAND    <k_DOS,        c_DOS,        PCT_REG>
  70.         COMMAND    <k_Else,    c_Else,        PCT_ELSE>
  71.         COMMAND    <k_EndIf,    c_EndIf,    PCT_FI>
  72.         COMMAND    <k_Env,        c_Env,        PCT_REG>
  73.         COMMAND    <k_Flush,    c_Flush,    PCT_REG>
  74.         COMMAND    <k_GetKey,    c_GetKey,    PCT_REG>
  75.         COMMAND    <k_Go,        c_Go,        PCT_REG>
  76.         COMMAND    <k_IfAfter,    c_IfAfter,    PCT_IF>
  77.         COMMAND    <k_IfBefore,    c_IfBefore,    PCT_IF>
  78.         COMMAND    <k_IfKey,    c_IfKey,    PCT_IF>
  79.         COMMAND    <k_IfLoad,    c_IfLoad,    PCT_IF>
  80.         COMMAND    <k_IfScreen,    c_IfScreen,    PCT_IF>
  81. jump_command    COMMAND    <k_Jump,    c_Jump,        PCT_REG>
  82.         COMMAND    <k_Key,        c_Key,        PCT_REG>
  83. ;;;        COMMAND    <k_KeyFile,    c_KeyFile,    PCT_REG>
  84. label_command    COMMAND    <k_Label,    c_Label,    PCT_REG>
  85.         COMMAND    <k_LeftShift,    c_LeftShift,    PCT_REG>
  86.         COMMAND    <k_Load,    c_Load,        PCT_REG>
  87.         COMMAND    <k_Lock,    c_Lock,        PCT_REG>
  88.         COMMAND    <k_Mode,    c_Mode,        PCT_REG>
  89.         COMMAND    <k_Numlock,    c_NumLock,    PCT_REG>
  90.         COMMAND    <k_Pause,    c_Pause,    PCT_REG>
  91.         COMMAND    <k_PrintScreen,    c_PrintScreen,    PCT_REG>
  92.         COMMAND    <k_Output,    c_Output,    PCT_REG>
  93.         COMMAND    <k_Quit,    c_Quit,        PCT_REG>
  94.         COMMAND    <k_RightShift,    c_RightShift,    PCT_REG>
  95.         COMMAND    <k_Screen,    c_Screen,    PCT_REG>
  96.         COMMAND    <k_ScrollLock,    c_ScrollLock,    PCT_REG>
  97. setif_command    COMMAND    <k_SetIf,    c_SetIf,    PCT_REG>
  98.         COMMAND    <k_SetMemory,    c_SetMemory,    PCT_REG>
  99.         COMMAND    <k_Tone,    c_Tone,        PCT_REG>
  100.         COMMAND    <k_TypeRate,    c_TypeRate,    PCT_REG>
  101.         COMMAND    <k_Unlock,    c_Unlock,    PCT_REG>
  102.         COMMAND    <k_Video,    c_Video,    PCT_REG>
  103.         COMMAND    <k_WaitChild,    c_WaitChild,    PCT_REG>
  104.         COMMAND    <k_WaitCursor,    c_WaitCursor,    PCT_REG>
  105.         COMMAND    <k_WaitScreen,    c_WaitScreen,    PCT_REG>
  106.         COMMAND    <k_WaitUntil,    c_WaitUntil,    PCT_REG>
  107.         COMMAND    <k_Wipe,    c_Wipe,        PCT_REG>
  108.  
  109. JUMP_INDEX    =    (jump_command - command_table) / SIZE COMMAND
  110. LABEL_INDEX    =    (label_command - command_table) / SIZE COMMAND
  111. SETIF_INDEX    =    (setif_command - command_table) / SIZE COMMAND
  112.  
  113. ; Command keywords
  114.  
  115. command_keys    LABEL    BYTE
  116. k_Alt        db    "Alt",0
  117. k_Break        db    "Break",0
  118. k_CapsLock    db    "CapsLock",0
  119. k_Ctrl        db    "Ctrl",0
  120. k_Cursor    db    "Cursor",0
  121. k_DOS        db    "DOS",0
  122. k_Else        db    "Else",0
  123. k_EndIf        db    "EndIf",0
  124. k_Env        db    "Env",0
  125. k_Flush        db    "Flush",0
  126. k_GetKey    db    "GetKey",0
  127. k_Go        db    "Go",0
  128. k_IfAfter    db    "IfAfter",0
  129. k_IfBefore    db    "IfBefore",0
  130. k_IfKey        db    "IfKey",0
  131. k_IfLoad    db    "IfLoad",0
  132. k_IfScreen    db    "IfScreen",0
  133. k_Jump        db    "Jump",0
  134. k_Key        db    "Key",0
  135. ;;;k_KeyFile    db    "KeyFile",0
  136. k_Label        db    "Label",0
  137. k_LeftShift    db    "LeftShift",0
  138. k_Load        db    "Load",0
  139. k_Lock        db    "Lock",0
  140. k_Mode        db    "Mode",0
  141. k_NumLock    db    "NumLock",0
  142. k_Pause        db    "Pause",0
  143. k_PrintScreen    db    "PrintScreen",0
  144. k_Output    db    "Output",0
  145. k_Quit        db    "Quit",0
  146. k_RightShift    db    "RightShift",0
  147. k_Screen    db    "Screen",0
  148. k_ScrollLock    db    "ScrollLock",0
  149. k_SetIf        db    " SetIf",0        ; cannot be written
  150. k_SetMemory    db    "SetMemory",0
  151. k_Tone        db    "Tone",0
  152. k_TypeRate    db    "TypeRate",0
  153. k_Unlock     db    "Unlock",0
  154. k_Video        db    "Video",0
  155. k_WaitChild    db    "WaitChild",0
  156. k_WaitCursor    db    "WaitCursor",0
  157. k_WaitScreen    db    "WaitScreen",0
  158. k_WaitUntil    db    "WaitUntil",0
  159. k_Wipe        db    "Wipe",0
  160.         db    0        ; end of table marker
  161.  
  162. ; Key table for "On"/"Off" arguments:
  163.  
  164. on_off        db    'OFF',0,'ON',0,0    ; Off is 0, On is 1
  165.  
  166. ; Key table for arguments to the Env[ironment] command:
  167.  
  168. env_args    db    'OWN',0,'MASTER',0,0    ; Own is 0, Master is 1
  169.  
  170. ; Dispatch table for preprocessing commands by type
  171.  
  172. preprocessing_table    LABEL    WORD
  173.         dw    pp_regular, pp_If, pp_Else, pp_EndIf
  174.  
  175. ; Extra dispatch table for conditional commands
  176.  
  177. n_table        dw    n_Nop, n_If, c_Else, c_EndIf
  178.  
  179. ; Miscellaneous stuff
  180.  
  181. pan_extension    db    '.PAN',0    ; Standard extension for Pan scripts
  182. pan_sp        dw    0        ; SP on transferring to a child program
  183. allocated_block    dw    0        ; segment of an allocated block
  184. break_condition    db    0        ; ? break on or off
  185. command_ptr    dw    script_buffer
  186. current_command    dw    0        ; pointer to current command in script_buffer
  187. DOS_buffer    db    128 DUP (0)    ; for use by DOS command (see c_DOS)
  188. env_inherit    db    0        ; ? environment that children will get
  189.                     ; default is Pandora's own
  190. file_handle    dw    ?        ; handle for command file
  191. if_condition    db    0        ; IF condition
  192. if_effect_level    db    0        ; Level at which last If was TRUE
  193. if_nest_level    db    0        ; IF condition level
  194. in_pan_flag    db    0        ; set non-zero when in Pandora timer intercept
  195. init_video_mode    db    0        ; initial video mode
  196. key_getter    dw    0        ; function to load a key
  197. keyboard_feed    db    0        ; set when Pandora needs exclusive access
  198.                     ; to the keyboard
  199. keyboard_state    db    0        ; 0 => unlocked, 1 => locked
  200. keyfile_handle    dw    0        ; handle for KeyFile file
  201. kbb_segment    dw    KBB_SEGADD    ; memory segment of keyboard buffer
  202.  
  203. line_count    db    0        ; byte for char count in line_buffer
  204. line_buffer    db    128 dup (?)    ; buffer for reading text through
  205.  
  206. pan_state    db    PS_INITIAL    ; see list of PS_xxxx states above
  207. screen_columns    db    0        ; number of columns displayed in current video mode
  208. recall_address    dw    0        ; address to recall after timer expiry
  209. time_out    dw    0        ; time_out counter (ticks)
  210. type_rate    dw    0        ; simulation rate for typing
  211. va        db    70h        ; video attribute, default like DOS MDA
  212. video_mode    db    0        ; current video mode
  213. video_segment    dw    0        ; memory segment address of video buffer
  214.  
  215. ; Saved BIOS-keyboard interrupt vector
  216.  
  217. i_BIOS_kb    LABEL    dword    
  218. x_bk_offset    dw    0
  219. x_bk_segment    dw    0
  220.  
  221. ; Saved timer interrupt vector
  222.  
  223. i_timer        LABEL    dword
  224. x_timer_offset    dw    0
  225. x_timer_segment    dw    0
  226.  
  227. ; Saved keyboard interrupt vector
  228.  
  229. i_keyboard    LABEL    dword
  230. x_key_offset    dw    0
  231. x_key_segment    dw    0
  232.  
  233. ; Saved Ctrl-Break interrupt vector
  234.  
  235. i_ctrl_break    LABEL    dwORD
  236. x_break_offset    dw    0
  237. x_break_segment    dw    0
  238.  
  239. ; Stack pointer from intercept
  240.  
  241. callers_sp    dw    0
  242. callers_ss    dw    0
  243.  
  244. ; Last keypress obtained by a GetKey command
  245.  
  246. keypress    LABEL    WORD    
  247. key_ASCII    db    0
  248. key_scan    db    0
  249.  
  250. ; Screen position
  251.  
  252. screen_position    LABEL    word
  253. n_col        db    0    ; column number
  254. n_row        db    0    ; row number
  255.  
  256. ; "Keyboard" Input Queue pointers
  257.  
  258. kiq_first    dw    0    ; pointer to first/next character
  259.  
  260. ; Hour and minute for WaitUntil command
  261.  
  262. time_argument    LABEL    WORD
  263. minute        db    0    ; minute to wait for (0 - 60)
  264. hour        db    0    ; hour to wait for (0 - 24)
  265.  
  266. ; Parameter block for DOS program-load function
  267.  
  268. parameter_block    LABEL    WORD
  269. env_seg        dw    0    ; segment of environment string
  270. p_command_line    LABEL    DWORD    ; pointer to command line
  271. command_offset    dw    0
  272. command_segment    dw    0
  273. FCB1        LABEL    dwORD    ; FCB pointers
  274. FCB1_O        dw    0
  275. FCB1_S        dw    0
  276. FCB2        LABEL    dwORD
  277. FCB2_O        dw    0
  278. FCB2_S        dw    0
  279. child_sp    dw    0    ; child's SP
  280. child_ss    dw    0    ; child's SS
  281. child_ip    dw    0    ; child's IP
  282. child_cs    dw    0    ; child's CS
  283.  
  284. ; Other information about the child process
  285.  
  286. child_psp    dw    0    ; segment of child's PSP
  287. child_size    dw    0    ; size in paragraphs
  288.  
  289. ; Video mode table
  290.  
  291. vseg_table    LABEL    BYTE            ; Mode    Type
  292.         db    0B8h        ; 0:  CGA 40x25 b/w
  293.         db    0B8h        ; 1:  CGA 40x25 16 colors
  294.         db    0B8h        ; 2:  CGA 80x25 b/w
  295.         db    0B8h        ; 3:  CGA 80x25 16 colors
  296.         db    0        ; 4:  CGA graphics mode
  297.         db    0        ; 5:  CGA graphics mode
  298.         db    0        ; 6:  CGA graphics mode
  299.         db    0B0h        ; 7:  MDA 80x25 b/w
  300.  
  301. ; Translation table:  ASCII codes into keyboard scan codes
  302.  
  303. scan    db    03, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24
  304. ;              Nul  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O
  305.     db    25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 01, 26, 53, 27, 12
  306. ;        ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z Esc  FS  GS  RS  US
  307.     db    57, 02, 40, 04, 05, 06, 08, 40, 10, 11, 09, 13, 51, 12, 52, 53
  308. ;        sp   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
  309.     db    11, 02, 03, 04, 05, 06, 07, 08, 09, 10, 39, 39, 51, 13, 52, 53
  310. ;         0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
  311.     db    03, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24
  312. ;         @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
  313.     db    25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 26, 43, 27, 07, 12
  314. ;         P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
  315.     db    41, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24
  316. ;         `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
  317.     db    25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 26, 43, 27, 41, 14
  318. ;         p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ Del
  319.  
  320. ; Translation table for special keys
  321.  
  322. keyname_list    LABEL    BYTE
  323.     db    'ESC',0,'TAB',0,'ENTER',0
  324.     db    'F10',0
  325.     db    'F1',0,'F2',0,'F3',0,'F4',0,'F5',0,'F6',0,'F7',0,'F8',0,'F9',0
  326.     db    'HOME',0,'UP',0,'PGUP',0,'LEFT',0
  327.     db    'RIGHT',0,'END',0,'DOWN',0,'PGDN',0,'INS',0,'DEL',0
  328.     db    0
  329.  
  330. shiftname_list    LABEL    BYTE
  331.     db    'ALT',0,'CTRL',0,'SHIFT',0,0
  332.  
  333. shiftbits    LABEL    BYTE
  334.     db    08h, 04h, 02h
  335.  
  336. key_scans    LABEL    BYTE
  337.     db    1    ; Escape
  338.     db    15    ; Tab
  339.     db    28    ; Enter
  340.     db    68    ; F10
  341.     db    59,60,61,62,63,64,65,66,67    ; F1 - F9
  342.     db    71    ; Home
  343.     db    72    ; Up Arrow
  344.     db    73    ; Page Up
  345.     db    75    ; Left Arrow
  346.     db    77    ; Right Arrow
  347.     db    79    ; End
  348.     db    80    ; Down Arrow
  349.     db    81    ; Page Down
  350.     db    82    ; Insert
  351.     db    83    ; Delete
  352.  
  353. ; Shift tables
  354.  
  355. No_shift    LABEL    WORD
  356.     dw    0000h, 011Bh, 0231h, 0332h, 0433h, 0534h, 0635h, 0736h
  357.     dw    0837h, 0938h, 0A39h, 0B30h, 0C2Dh, 0D3Dh, 0E08h, 0F09h
  358.     dw    1071h, 1177h, 1265h, 1372h, 1474h, 1579h, 1675h, 1769h
  359.     dw    186Fh, 1970h, 1A5Bh, 1B5Dh, 1C0Dh, 0000h, 1E61h, 1F73h
  360.     dw    2064h, 2166h, 2267h, 2368h, 246Ah, 256Bh, 266Ch, 273Bh
  361.     dw    2827h, 2960h, 0000h, 2B5Ch, 2C7Ah, 2D78h, 2E63h, 2F76h
  362.     dw    3062h, 316Eh, 326Dh, 332Ch, 342Eh, 352Fh, 0000h, 372Ah
  363.     dw    0000h, 3920h, 0000h, 3B00h, 3C00h, 3D00h, 3E00h, 3F00h
  364.     dw    4000h, 4100h, 4200h, 4300h, 4400h, 0000h, 0000h, 4700h
  365.     dw    4800h, 4900h, 4A2Dh, 4B00h, 0000h, 4D00h, 4E2Bh, 4F00h
  366.     dw    5000h, 5100h, 5200h, 5300h
  367.  
  368. Shift_shift    LABEL    WORD
  369.     dw    0000h, 011Bh, 0221h, 0340h, 0423h, 0524h, 0625h, 075Eh
  370.     dw    0826h, 092Ah, 0A28h, 0B29h, 0C5Fh, 0D2Bh, 0E08h, 0F00h
  371.     dw    1051h, 1157h, 1245h, 1352h, 1454h, 1559h, 1655h, 1749h
  372.     dw    184Fh, 1950h, 1A7Bh, 1B7Dh, 1C0Dh, 0000h, 1E41h, 1F53h
  373.     dw    2044h, 2146h, 2247h, 2348h, 244Ah, 254Bh, 264Ch, 273Ah
  374.     dw    2822h, 297Eh, 0000h, 2B7Ch, 2C5Ah, 2D58h, 2E43h, 2F56h
  375.     dw    3042h, 314Eh, 324Dh, 333Ch, 343Eh, 353Fh, 0000h, 0000h
  376.     dw    0000h, 3920h, 0000h, 5400h, 5500h, 5600h, 5700h, 5800h
  377.     dw    5900h, 5A00h, 5B00h, 5C00h, 5D00h, 0000h, 0000h, 4737h
  378.     dw    4838h, 4939h, 4A2Dh, 4B34h, 4C35h, 4D36h, 4E2Bh, 4F31h
  379.     dw    5032h, 5133h, 5230h, 532Eh
  380.  
  381. Ctrl_shift    LABEL    WORD
  382.     dw    0000h, 011Bh, 0000h, 0300h, 0000h, 0000h, 0000h, 071Eh
  383.     dw    0000h, 0000h, 0000h, 0000h, 0C1Fh, 0000h, 0E7Fh, 0000h
  384.     dw    1011h, 1117h, 1205h, 1312h, 1414h, 1519h, 1615h, 1709h
  385.     dw    180Fh, 1910h, 1A1Bh, 1B1Dh, 1C0Ah, 0000h, 1E01h, 1F13h
  386.     dw    2004h, 2106h, 2207h, 2308h, 240Ah, 250Bh, 260Ch, 0000h
  387.     dw    0000h, 0000h, 0000h, 2B1Ch, 2C1Ah, 2D18h, 2E03h, 2F16h
  388.     dw    3002h, 310Eh, 320Dh, 0000h, 0000h, 0000h, 0000h, 3710h
  389.     dw    0000h, 3920h, 0000h, 5E00h, 5F00h, 6000h, 6100h, 6200h
  390.     dw    6300h, 6400h, 6500h, 6600h, 6700h, 0000h, 0000h, 7700h
  391.     dw    0000h, 8400h, 0000h, 7300h, 0000h, 7400h, 0000h, 7500h
  392.     dw    0000h, 7600h, 0000h, 0000h
  393.  
  394. Alt_shift    LABEL    WORD
  395.     dw    0000h, 0000h, 7800h, 7900h, 7A00h, 7B00h, 7C00h, 7D00h
  396.     dw    7E00h, 7F00h, 8000h, 8100h, 8200h, 8300h, 0000h, 0000h
  397.     dw    1000h, 1100h, 1200h, 1300h, 1400h, 1500h, 1600h, 1700h
  398.     dw    1800h, 1900h, 0000h, 0000h, 0000h, 0000h, 1E00h, 1F00h
  399.     dw    2000h, 2100h, 2200h, 2300h, 2400h, 2500h, 2600h, 0000h
  400.     dw    0000h, 0000h, 0000h, 0000h, 2C00h, 2D00h, 2E00h, 2F00h
  401.     dw    3000h, 3100h, 3200h, 0000h, 0000h, 0000h, 0000h, 0000h
  402.     dw    0000h, 3920h, 0000h, 6800h, 6900h, 6A00h, 6B00h, 6C00h
  403.     dw    6D00h, 6E00h, 6F00h, 7000h, 7100h, 0000h, 0000h, 0000h
  404.     dw    0000h, 0000h, 0000h, 0000h, 0000h, 0000h, 0000h, 0000h
  405.     dw    0000h, 0000h, 0000h, 0000h
  406.  
  407. ;******************************************************************************
  408. ;*                                                                            *
  409. ;*                     Interrupt-Intercept Procedures                         *
  410. ;*                                                                            *
  411. ;******************************************************************************
  412.  
  413. ;    timer-interrupt intercept
  414.  
  415. timer_intercept    proc    far
  416.     pushf                ; simulate another interrupt
  417.     call    cs:i_timer        ;    to let timer do its thing
  418.     push    ax
  419.     mov    al,1
  420.     xchg    al,cs:in_pan_flag    ; check we're not already here
  421.     or    al,al
  422.     jnz    .tim4            ; exit immediately if so
  423.  
  424.     mov    ax,sp            ; switch stacks
  425.     mov    cs:callers_sp,sp
  426.     mov    ax,ss
  427.     mov    cs:callers_ss,ax
  428.     mov    ax,cs
  429.     mov    ss,ax
  430.     mov    sp,OFFSET interrupt_stack
  431.     sti                ; allow interrupts
  432.  
  433.     push    bx            ; save all registers
  434.     push    cx
  435.     push    dx
  436.     push    si
  437.  
  438.     push    di
  439.     push    ds
  440.     push    es
  441.     push    bp
  442.     mov    ax,cs            ; set DS and ES to Pandora segment
  443.     mov    ds,ax
  444.     mov    es,ax
  445.     cld
  446.  
  447.     mov    ax,time_out        ; AX = number of ticks to timeout
  448.     test    ax,ax            ; are we in a waiting period?
  449.     jz    .tim2            ; if no waiting
  450.     dec    time_out        ; else count down the ticks
  451.     jnz    .tim3            ; if more to go
  452.  
  453. .tim2:    call    timer_call
  454.  
  455. .tim3:    pop    bp            ; restore state
  456.     pop    es
  457.     pop    ds
  458.     pop    di
  459.     pop    si
  460.     pop    dx
  461.     pop    cx
  462.     pop    bx
  463.  
  464.     cli                ; turn off interrupts
  465.     mov    ax,cs:callers_sp    ; restore the interruptee's stack
  466.     mov    sp,ax
  467.     mov    ax,cs:callers_ss
  468.     mov    ss,ax
  469.     mov    cs:in_pan_flag,0    ; and reset in-Pandora flag
  470.  
  471. .tim4:    pop    ax
  472.     iret
  473. timer_intercept    endp
  474.  
  475. ;    Called every time the timer interrupt is intercepted.
  476.  
  477. timer_call    proc    near
  478.     mov    ax,recall_address
  479.     test    ax,ax            ; need to do a recall?
  480.     jz    .tc1            ; no
  481.     call    ax            ; yes, do it
  482.     ret
  483.  
  484. .tc1:    call    interpret        ; process a new command
  485.     cmp    time_out,0        ; check number of ticks to timeout
  486.     je    .tc1            ; if no wait then do another
  487.     ret
  488. timer_call    endp
  489.  
  490. ;     Keyboard interrupt intercept.  Every time a keyboard interrupt
  491. ;    occurs we mess with the pointers to make it seem that the BIOS
  492. ;    keyboard-input queue is full.  This allows a Ctrl-Alt-Del to
  493. ;    take effect but for all normal keypresses the user will get a
  494. ;    beep.
  495.  
  496. keyboard_intercept    proc    far
  497.     push    ax
  498.     push    ds
  499.     mov    ds,cs:kbb_segment    ; DS = keyboard-buffer segment
  500.     mov    ax,ds:[KBB_TAIL]    ; get tail
  501.     inc    ax            ; bump tail pointer
  502.     inc    ax
  503.     cmp    ax,ds:[KBB_END]
  504.     jne    .ki1
  505.     mov    ax,ds:[KBB_START]    ; if wrapped around
  506.  
  507. .ki1:    xchg    ax,ds:[KBB_HEAD]    ; make it look like there's no room
  508.     pushf                ; fake interrupt to real handler
  509.     call    cs:i_keyboard
  510.     xchg    ax,ds:[KBB_HEAD]    ; replace "real" head of queue
  511.     pop    ds
  512.     pop    ax
  513.     iret                ; disconnects the keyboard
  514. keyboard_intercept    endp
  515.  
  516. ;    BIOS-keyboard interrupt intercept
  517.  
  518. BIOS_kb_intercept    proc    far
  519.     pushf
  520.     cmp    ah,01h            ; Function 0 or 1?
  521.     ja    .kbi2            ; no, let BIOS handle it
  522.     sti                ; ensure interrupts can happen
  523.     je    .kbi1
  524.  
  525. ; Handle function 00h:  Read Character from Keyboard.  If Pandora has locked the
  526. ; keyboard then we delay the process until the lock is released.  If Pandora has
  527. ; not locked the keyboard then we check if a character is available; if it is
  528. ; then we let the BIOS complete the request, else keep waiting in case Pandora
  529. ; locks the keyboard.
  530.  
  531. .kbi0:    test    cs:keyboard_feed,0FFh    ; has Pandora reserved the keyboard?
  532.     jnz    .kbi0
  533.  
  534.     mov    ah,01h            ; BIOS Get Keyboard Status
  535.     pushf
  536.     call    cs:[i_BIOS_kb]
  537.     jz    .kbi0
  538.     mov    ah,00h
  539.     jmp    SHORT .kbi2
  540.  
  541. ; Handle function 01h:  Get Keyboard Status.  If Pandora has locked the keyboard
  542. ; then we return a no-character-waiting indication to the process.  If Pandora
  543. ; has not locked the keyboard then we let the BIOS handle the request.
  544.  
  545. .kbi1:    test    cs:keyboard_feed,0FFh    ; has Pandora reserved the keyboard?
  546.     jz    .kbi2            ; no, go to BIOS
  547.     popf
  548.     xor    ax,ax            ; yes, return with no input indication
  549.     retf    2
  550.  
  551. .kbi2:    popf
  552.     jmp    cs:[i_BIOS_kb]
  553. BIOS_kb_intercept    endp
  554.  
  555. ; Ctrl-Break intercept
  556.  
  557. ctrl_break_intercept    proc    far
  558.     iret
  559. ctrl_break_intercept    endp
  560.  
  561. ;******************************************************************************
  562. ;*                                                                            *
  563. ;*                               Entry Code                                   *
  564. ;*                                                                            *
  565. ;******************************************************************************
  566.  
  567.     assume    ds:code
  568. main    proc    near
  569.     cld
  570.     mov    sp,OFFSET regular_stack    ; set internal stack
  571.  
  572.     mov    dx,OFFSET initmsg    ; announce program
  573.     mov    ah,9h
  574.     int    21h
  575.     call    c_Mode            ; determine video mode
  576.     mov    al,video_mode        ; save initial video mode
  577.     mov    init_video_mode,al
  578.     push    es
  579.     mov    ax,3516h        ; get interrupt vector for BIOS kb
  580.     int    21h            ; ES:BX -> BIOS kb service
  581.     mov    x_bk_offset,bx        ; save this for internal use
  582.     mov    ax,es
  583.     mov    x_bk_segment,ax
  584.     pop    es
  585.  
  586.     call    get_script        ; load the command file
  587.     jc    .mai3
  588.     mov    bx,OFFSET script_buffer    ; calculate paragraphs used
  589.     add    bx,ax            ; AX = size of script as loaded
  590.     add    bx,15            ; round up to a paragraph boundary
  591.     mov    cx,4
  592.     shr    bx,cl            ; convert to paragraphs
  593.     mov    ah,4Ah            ; DOS modify allocated memory blocks
  594.     int    21h
  595.     call    resolve_jumps        ; prepare the script
  596.     jc    .mai3            ; if an error was detected
  597.     mov    ax,OFFSET script_buffer    ; set command pointer
  598.     mov    command_ptr,ax
  599.  
  600. .mai1:    call    interpret        ; perform the first/next command
  601.  
  602. .mai2:    xor    cx,cx
  603.     xchg    cx,time_out        ; CX = timeout
  604.     test    cx,cx            ; did last command set a timeout?
  605.     jz    .mai1            ; if not continue processing
  606.     call    delay            ; else delay for the requisite period
  607.     call    [recall_address]    ; then call the completion code
  608.     jmp    SHORT .mai2        ; which can timeout again
  609.  
  610. .mai3:    mov    ah,9h            ; get here with SI -> error message
  611.     int    21h            ; have DOS display it
  612.  
  613. .mai4:    jmp    terminate        ; die
  614. main    endp
  615.  
  616. ;******************************************************************************
  617. ;*                                                                            *
  618. ;*                    Primary Pandora Command Interpreter                     *
  619. ;*                                                                            *
  620. ;******************************************************************************
  621.  
  622. interpret    proc    near
  623.     mov    si,command_ptr        ; SI -> next command
  624.     xor    ax,ax
  625.     lodsb                ; AX = command length
  626.     test    al,al            ; zero-length command => end of script
  627.     jz    .int3
  628.     add    command_ptr,ax        ; update the command pointer
  629.     mov    current_command,si    ; save pointer to current command
  630.     lodsb                ; AX = command index
  631.     mul    command_entry_size    ; convert to table offset
  632.     mov    bx,ax            ; BX = entry offset
  633.     xor    ax,ax
  634.     or    al,if_condition
  635.     jnz    .int1            ; if processing off
  636.                 ; call the command processor with AX = 0
  637.     call    WORD PTR [command_table+PC_PROC+bx]
  638.     ret
  639.  
  640. .int1:    xor    ax,ax            ; get AX = command type
  641.     mov    al,BYTE PTR [command_table+PC_TYPE+bx]
  642.     mov    bx,ax            ; and call corresponding proc
  643.     call    [n_table+bx]
  644.  
  645. .int2:    ret
  646.  
  647. .int3:    jmp    c_Quit            ; Quit on end of script
  648. interpret    endp
  649.  
  650. ;******************************************************************************
  651. ;*                                                                            *
  652. ;*                 Procedures for performing Pandora commands                 *
  653. ;*                                                                            *
  654. ;******************************************************************************
  655.  
  656. ;    Alt ON/OFF
  657.  
  658. c_Alt    proc    near
  659.     mov    dl,08h            ; DL = Alt bit in keyboard flags
  660.     jmp    ShiftLock
  661. c_Alt    endp
  662.  
  663. ;    Break On/Off
  664.  
  665. c_Break    proc    near
  666.     mov    bx,OFFSET on_off    ; BX -> "ON/OFF"
  667.     call    match_key        ; check the argument
  668.     jne    .cb1            ; if not "ON" nor "OFF"
  669.     mov    break_condition,al    ; else index sets the break condition
  670.     ret
  671.  
  672. .cb1:    mov    si,OFFSET .cbmsg
  673.     jmp    command_error
  674.  
  675. .cbmsg    db    'Break should have argument "On" or "Off"',0
  676. c_Break    endp
  677.  
  678. ;    Else - if Else belongs to the last If processed then reverse the
  679. ;           current if condition
  680.  
  681. c_Else    proc    near
  682.     mov    al,if_nest_level    ; is this else effective?
  683.     cmp    al,if_effect_level
  684.     ja    .cel1            ; ignore if not
  685.     not    if_condition        ; switch the condition marker
  686.  
  687. .cel1:    ret
  688. c_Else    endp
  689.  
  690. ;    CapsLock ON/OFF
  691.  
  692. c_CapsLock    proc    near
  693.     mov    dl,40h            ; DL = CapsLock bit in keyboard flags
  694.     jmp    ShiftLock
  695. c_CapsLock    endp
  696.  
  697. ;    Ctrl ON/OFF
  698.  
  699. c_Ctrl    proc    near
  700.     mov    dl,04h            ; DL = Alt bit in keyboard flags
  701.     jmp    ShiftLock
  702. c_Ctrl    endp
  703.  
  704. ;    Cursor <row> <column> - move the cursor to the given position.
  705.  
  706. c_Cursor    proc    near
  707.     call    get_screen_position    ; decode row and column
  708.     mov    ah,02h            ; BIOS Set Cursor Position
  709.     xor    bx,bx            ; assume page 0
  710.     mov    dx,screen_position    ; DH = row, DL = column
  711.     int    10h
  712.     ret
  713. c_Cursor    endp
  714.  
  715. ;    DOS "command"  - execute a command via the DOS command interpreter.
  716. ;
  717.  
  718. c_DOS    proc    near
  719.     cmp    pan_state,PS_RUNNING    ; is state suitable for DOS call?
  720.     jae    .dos3            ; give error if it's not
  721.     call    normalize        ; normalize the argument
  722.     mov    di,OFFSET DOS_buffer
  723.     mov    si,OFFSET DOS_options    ; copy " /c "
  724.     call    copy_string
  725.     mov    si,OFFSET line_buffer    ; and copy the user's command
  726.     call     copy_string
  727.     mov    WORD PTR es:[di],000Dh    ; append a CR and null
  728.     mov    command_offset,OFFSET DOS_buffer
  729.     mov    command_segment,ds    ; set up parameter block
  730.     mov    ax,cs:[2Ch]        ; AX = environment segment
  731.     mov    env_seg,ax
  732.     mov    si,OFFSET comspec    ; search env for "COMSPEC"
  733.     call    search_env        ; returns DS:SI -> string if found
  734.     mov    dx,si            ; DOS needs DS:DX -> program file
  735.     jc    .dos1            ; if not found
  736.     mov    ax,4B00h        ; DOS Load and Execute a Program
  737.     mov    bx,OFFSET parameter_block
  738.     int    21h
  739.     push    cs            ; restore DS
  740.     pop    ds
  741.     jc    .dos2            ; if Exec failed
  742.     ret
  743.  
  744. .dos1:    mov    si,OFFSET .dosmsg1    ; complain about command interpreter
  745.     jmp    SHORT .dos4
  746.  
  747. .dos2:    mov    si,OFFSET .dosmsg2
  748.     cmp    ax,8            ; insufficient memory?
  749.     je    .dos4
  750.     mov    si,OFFSET .dosmsg3    ; complain that Exec failed
  751.     jmp    SHORT .dos4
  752.  
  753. .dos3:    mov    si,OFFSET .dosmsg4    ; complain about the state of things
  754.  
  755. .dos4:    jmp    command_error
  756.  
  757. .dosmsg1    db    'COMSPEC not found in environment',0
  758. .dosmsg2    db    "Insufficient memory to load command interpreter",0
  759. .dosmsg3    db    "Execution of command interpreter failed",0
  760. .dosmsg4    db    "May not be used while running a program",0
  761. comspec    db    "COMSPEC",0
  762.  
  763. DOS_options    db    " /c ",0
  764. c_DOS    endp
  765.  
  766. ;    EndIf - terminate an IF clause
  767.  
  768. c_EndIf    proc    near
  769.     cmp    if_nest_level,0        ; is EndIf appropriate?
  770.     jz    .cen1            ; ignore if not (should be impossible)
  771.     mov    al,if_nest_level    ; if this EndIf effective?
  772.     dec    if_nest_level        ; count out one level
  773.     cmp    al,if_effect_level
  774.     jne    .cen1            ; if not there is no more to do
  775.     dec    if_effect_level
  776.     mov    if_condition,0        ; process!
  777.  
  778. .cen1:    ret
  779. c_EndIf    endp
  780.  
  781. ;    Env Master/Own
  782. ;
  783. ; Set environment to be inherited by children:
  784. ;    "Own" means each child gets a copy of Pandora's environment.
  785. ;    "Master" means each child get a copy of the master environment.
  786.  
  787. c_Env    proc    near
  788.     mov    bx,OFFSET env_args    ; BX -> "MASTER/OWN"
  789.     call    match_key        ; check the argument
  790.     jne    .cenv1            ; if not valid
  791.     mov    env_inherit,al        ; else index sets the condition
  792.     ret
  793.  
  794. .cenv1:    mov    si,OFFSET .cenvm
  795.     jmp    command_error
  796.  
  797. .cenvm    db    'Should have argument "Master" or "Own"',0
  798. c_Env    endp
  799.  
  800. ;    Flush - flush keypress buffer
  801.  
  802. c_Flush    proc    near
  803.  
  804. .cf1:    mov    ah,01h            ; check for keyboard input
  805.     pushf                ; by emulating interrupt to the BIOS
  806.     call    [i_BIOS_kb]        ; int    16h
  807.     jz    .cf2            ; if no input
  808.     xor    ax,ax            ; else read that input
  809.     pushf
  810.     call    [i_BIOS_kb]        ; int    16h
  811.     jmp    SHORT .cf1        ; keep checking until there is none
  812.  
  813. .cf2:    ret
  814. c_Flush    endp
  815.  
  816. ;    GetKey - input a keypress
  817.  
  818. c_GetKey    proc    near
  819.     cmp    pan_state,PS_RUNNING    ; target program in action?
  820.     je    .gk3            ; yes, get a keypress by stealth
  821.     mov    ah,00h            ; else just use BIOS service
  822.     int    16h
  823.     mov    keypress,ax        ; save the codes
  824.     cmp    break_condition,0    ; break mode on?
  825.     jz    .gk1            ; no, don't handle aborts specially
  826.     cmp    ax,2E03h        ; Control-C?
  827.     je    .gk2            ; quit if so
  828.  
  829. .gk1:    ret
  830.  
  831. .gk2:    jmp    c_Quit
  832.  
  833. .gk3:    inc    time_out        ; check on every tick
  834.     mov    recall_address,OFFSET .gk4    ; come back at label .gk4
  835.     inc    keyboard_feed        ; lock the keyboard
  836.     ret
  837.  
  838. .gk4:    mov    ah,01h            ; check for keyboard input
  839.     pushf
  840.     call    [i_BIOS_kb]        ; int    16h
  841.     jz    .gk5            ; if none
  842.     xor    ax,ax            ; read that input
  843.     pushf
  844.     call    [i_BIOS_kb]        ; int    16h
  845.     mov    keypress,ax        ; save it
  846.     dec    keyboard_feed        ; release the keyboard
  847.     mov    recall_address,0    ; no more recall
  848.     ret
  849.  
  850. .gk5:    inc    time_out        ; continue waiting
  851.     ret
  852. c_GetKey    endp
  853.  
  854. ;    Go - initiate execution of a loaded program
  855.  
  856. c_Go    proc    near
  857.     cmp    pan_state,PS_LOADED    ; check that state is correct
  858.     je    .go2            ; if okay...
  859.     mov    si,OFFSET .gomsg2    ; "Program already running"
  860.     jg    .go1            ; error if Go done already
  861.     mov    si,OFFSET .gomsg1    ; "No program loaded"
  862.  
  863. .go1:    jmp    command_error
  864.  
  865. .gomsg1    db    'No program loaded',0
  866. .gomsg2    db    'Program already running',0
  867.  
  868. ; Copy command line to child's PSP
  869.  
  870. .go2:    call    normalize        ; copy command line
  871.     mov    es,child_psp        ; ES = PSP of child
  872.     mov    di,81h            ; ES:DI -> command-line area
  873.     mov    al,' '            ; force a blank at the start
  874.     cmp    [si],al
  875.     je    .go3
  876.     stosb                ; good command lines start this way
  877.  
  878. .go3:    rep    movsb            ; copy command line
  879.     mov    BYTE PTR es:[di],CR    ; and append a carriage return
  880.     mov    ax,di            ; calculate length of command line
  881.     sub    al,81h
  882.     mov    es:[80h],al        ; and prepend length to the line
  883.  
  884. ; Set up default FCBs just in case
  885.  
  886.     push    ds
  887.     mov    ax,2901h        ; DOS Parse filename
  888.     mov    ds,child_psp
  889.     mov    si,81h            ; DS:SI -> command line to parse
  890.     mov    di,92            ; ES:DI -> place for 1st FCB
  891.     int    21h
  892.     mov    cx,ax            ; save drive valid flag
  893.     mov    ax,2901h        ; DOS Parse filename
  894.     mov    di,108            ; ES:DI -> place for 2nd FCB
  895.     int    21h
  896.     pop    ds
  897.  
  898.     mov    in_pan_flag,1        ; make intercepts ineffective
  899.     call    set_traps        ; set traps
  900.     mov    pan_state,PS_RUNNING    ; set state to running
  901.     jmp    run_it            ; and transfer control
  902. c_Go    endp
  903.  
  904. ;    IfAfter "HH:MM" - check if past given time of day
  905.  
  906. c_IfAfter    proc    near
  907.     call    decode_time
  908.     mov    ah,2Ch            ; DOS Get Time
  909.     int    21h
  910.     cmp    cx,time_argument    ; is it after the given time?
  911.     jae    iftrue
  912.     jb    iffalse
  913. c_IfAfter    endp
  914.  
  915. ;    IfBefore "HH:MM" - check if past given time of day
  916.  
  917. c_IfBefore    proc    near
  918.     call    decode_time
  919.     mov    ah,2Ch            ; DOS Get Time
  920.     int    21h
  921.     cmp    cx,time_argument    ; is it before the given time?
  922.     jb    iftrue
  923.     jae    iffalse
  924. c_IfBefore    endp
  925.  
  926. ;    IfKey "keylist" - check if last captured keystroke is in the given list.
  927.  
  928. c_IfKey    proc    near
  929.     call    normalize        ; copy and fix the string
  930.  
  931. .ifk1:    call    translate_key        ; get AX = key code
  932.     jc    iffalse            ; if no more keys in string
  933.     cmp    ax,keypress        ; is it what we captured?
  934.     je    iftrue
  935.     jne    .ifk1
  936. c_IfKey    endp
  937.  
  938. ;    IfLoad "program_name" - attempt to load the specified program and
  939. ;                set condition code according to result
  940.  
  941. c_IfLoad    proc    near
  942.     cmp    pan_state,PS_INITIAL    ; check that state is suitable
  943.     jne    .ifl1            ; if a program has already been loaded
  944.     call    loader            ; try the load
  945.     jc    iffalse            ; if load failed
  946.     jnc    iftrue
  947.  
  948. .ifl1:    mov    si,OFFSET .loadm    ; complain, complain, complain
  949.     jmp    command_error
  950.  
  951. .loadm    db    'A program is already loaded',0
  952. c_IfLoad    endp
  953.  
  954. ;    IfScreen <row> <column> "string" - check if "string" appears on screen
  955.  
  956. c_IfScreen    proc    near
  957.     call    get_screen_position    ; decode row and column
  958.     call    skip_whitespace        ; find the "string"
  959.     call    normalize        ; copy and normalize the string
  960.     call    check_screen        ; check if it's there
  961.     jnc    iftrue            ; if the string is there
  962.     jc    iffalse            ; if it's not
  963. c_IfScreen    endp
  964.  
  965. ;    Set If condition false
  966.  
  967. iffalse    proc    near
  968.     not    if_condition        ; inhibit processing
  969.                     ; and fall through
  970. iffalse    endp
  971.  
  972. ;    Set If condition true
  973.  
  974. iftrue    proc    near
  975.     inc    if_nest_level        ; count up one more If level
  976.     inc    if_effect_level        ; and active level
  977.     ret    
  978. iftrue    endp
  979.  
  980. ;    Jump label - transfer control to command following the named label.
  981.  
  982. c_Jump    proc    near
  983.     lodsw                ; AX -> destination
  984.     mov    command_ptr,ax        ; set new command pointer
  985.     ret
  986. c_Jump    endp
  987.  
  988. ;    Key "string" - make it appear as though "string" were typed.
  989.  
  990. c_Key    proc    near
  991.     call    copy_quoted_string    ; copy the string
  992.     mov    kiq_first,si        ; point to first character
  993.     mov    ax,OFFSET get_kiq    ; set proc to call for a keycode
  994.     mov    key_getter,ax
  995.     inc    time_out        ; continue on next tick
  996.     mov    recall_address,OFFSET stuff_keys    ; at proc stuff_keys
  997.     ret
  998. c_Key    endp
  999.  
  1000. ;    KeyFile "filename" - move contents of file to keyboard buffer
  1001. ;
  1002. ; Called with:
  1003. ;    SI -> name of file
  1004.  
  1005. c_KeyFile    proc    near
  1006.     cmp    pan_state,PS_RUNNING    ; check that state is correct
  1007.     jae    .kf1            ; if state unsuitable
  1008.     call    normalize        ; normalize the filename
  1009.     mov    ax,3D00h        ; DOS Open File
  1010.     mov    dx,si            ; DX -> filename
  1011.     int    21h
  1012.     jc    .kf2
  1013.     mov    keyfile_handle,ax    ; save the file handle
  1014.     mov    ax,OFFSET get_keyfile    ; set proc to call for a keycode
  1015.     mov    key_getter,ax
  1016.     inc    time_out        ; continue on next tick
  1017.     mov    recall_address,OFFSET stuff_keys    ; at proc stuff_keys
  1018.     ret
  1019.  
  1020. .kf1:    mov    si,OFFSET .kfmsg1
  1021.     jmp    command_error
  1022.  
  1023. .kf2:    mov    si,OFFSET .kfmsg2
  1024.     jmp    command_error
  1025.  
  1026. .kfmsg1    db    "KeyFile may not be used while running a program",0
  1027. .kfmsg2    db    "Error opening KeyFile file",0
  1028. c_KeyFile    endp
  1029.  
  1030. ;    Label name
  1031.  
  1032. c_Label    proc    near
  1033.     ret                ; no operation
  1034. c_Label    endp
  1035.  
  1036. ;    LeftShift ON/OFF
  1037.  
  1038. c_LeftShift    proc    near
  1039.     mov    dl,02h            ; DL = Left-Shift bit in keyboard flags
  1040.     jmp    ShiftLock
  1041. c_LeftShift    endp
  1042.  
  1043. ;    Load "program_name"
  1044. ;
  1045. ; Note:  During the load process, Pandora switches environment-table pointers so
  1046. ; that the loaded program inherits a copy of the master environment rather than
  1047. ; Pandora's own.  This trick allows DOS SET commands to be used in Pandora scripts to
  1048. ; set environment strings for automated programs.
  1049.  
  1050. c_Load    proc    near
  1051.     cmp    pan_state,PS_INITIAL    ; check that state is suitable
  1052.     jne    .cl2            ; if a program has already been loaded
  1053.     call    loader            ; attempt a load
  1054.     jc    bad_load        ; if load failed
  1055.  
  1056. .cl1:    ret                ; load successful, continue
  1057.  
  1058. .cl2:    jmp    .ifl1
  1059. bad_load:
  1060.     mov    dx,OFFSET .clA        ; "Cannot find target program"
  1061.     cmp    al,3            ; file or path not found?
  1062.     jle    .bl1
  1063.     mov    dx,OFFSET .clB        ; "Insufficient memory to load"
  1064.     cmp    al,8
  1065.     je    .bl1
  1066.     mov    dx,OFFSET .clC        ; "Cannot load target program"
  1067.  
  1068. .bl1:    mov    ah,9h
  1069.     int    21h
  1070.     call    ttyz            ; display filename
  1071.     jmp    c_Quit
  1072.  
  1073. .clA    db    'Pandora Error:  Cannot find target program:  $'
  1074. .clB    db    'Pandora Error:  Insufficient memory to load program:  $'
  1075. .clC    db    'Pandora Error:  Cannot load target program:  $'
  1076. c_Load    endp
  1077.  
  1078. ;    Lock - disconnect keyboard from application
  1079.  
  1080. c_Lock    proc    near
  1081.     cmp    keyboard_state,0    ; is keyboard already locked?
  1082.     jne    .loc1            ; if so this is a no-op
  1083.     inc    keyboard_state        ; else set state to locked
  1084.     mov    dx,OFFSET keyboard_intercept    ; replace keyboard interrupt
  1085.     mov    al,9h
  1086.     mov    bx,OFFSET i_keyboard
  1087.     call    set_vector
  1088.  
  1089.     mov    dx,OFFSET ctrl_break_intercept    ; replace Ctrl-Break interrupt
  1090.     mov    al,23h
  1091.     mov    bx,OFFSET i_ctrl_break
  1092.     call    set_vector
  1093.  
  1094. .loc1:    ret
  1095. c_Lock    endp
  1096.  
  1097. ;    Mode - force reassessment of current video mode
  1098.  
  1099. c_Mode    proc    near
  1100.     mov    ah,0Fh            ; BIOS Get Video Mode
  1101.     int    10h            ; returns AH = # columns, AL = mode,
  1102.                     ;    and BH = active page
  1103.     mov    video_mode,al        ; save the mode
  1104.     cmp    al,7            ; we only do text modes (0,1,2,3 and 7)
  1105.     ja    .cm1
  1106.     mov    screen_columns,ah    ; save number of columns on screen
  1107.     xor    ah,ah
  1108.     mov    bx,ax            ; BX = mode
  1109.     xor    ax,ax
  1110.     mov    ah,[vseg_table+bx]    ; AX = video buffer segment
  1111.     mov    video_segment,ax
  1112.     ret
  1113.  
  1114. ; Video mode that Pandora does not handle
  1115.  
  1116. .cm1:    mov    si,OFFSET .cmmsg
  1117.     jmp    command_error
  1118.  
  1119. .cmmsg    db    "Application set a video mode that Pandora cannot handle",0
  1120. c_Mode    endp
  1121.  
  1122. ;    NumLock ON/OFF
  1123.  
  1124. c_NumLock    proc    near
  1125.     mov    dl,20h            ; DL = NumLock bit in keyboard flags
  1126.     jmp    ShiftLock
  1127. c_NumLock    endp
  1128.  
  1129. ;    Output <string> - send a string to standard output
  1130.  
  1131. c_Output    proc    near
  1132.     cmp    pan_state,PS_RUNNING    ; is state suitable for DOS call?
  1133.     jae    .co2            ; ignore the command if it's not
  1134.     call    normalize        ; straighten up the string
  1135.  
  1136. .co1:    lodsb                ; AL = next character
  1137.     test    al,al
  1138.     jz    .co2            ; at end of string
  1139.     mov    ah,02h            ; DOS Display Output
  1140.     mov    dl,al            ; DL = character
  1141.     int    21h
  1142.     jmp    SHORT .co1        ; loop for all characters
  1143.  
  1144. .co2:    ret
  1145. c_Output    endp
  1146.  
  1147. ;    Pause <n> ticks/seconds/minutes - delay for a given period
  1148.  
  1149. c_Pause    proc    near
  1150.     call    decode_duration
  1151.     jc    .cp2
  1152.     mov    time_out,ax        ; set waiting time
  1153.     mov    recall_address,OFFSET .cp1    ; and set recall
  1154.     ret
  1155.  
  1156. .cp1:    mov    recall_address,0
  1157.     ret
  1158.  
  1159. .cp2:    mov    si,OFFSET .cpmsg    ; 'Pause 1-255 ticks, 1-255 seconds or 1-60 minutes'
  1160.     jmp    command_error
  1161.  
  1162. .cpmsg    db    'Pause 1-255 ticks, 1-255 seconds or 1-60 minutes',0
  1163. c_Pause    endp
  1164.  
  1165. ;    PrintScreen
  1166.  
  1167. c_PrintScreen    proc    near
  1168.     int    5            ; this is the trick
  1169.     ret
  1170. c_PrintScreen    endp
  1171.  
  1172. ;    Quit
  1173.  
  1174. c_Quit    proc    near
  1175.     call    unset_traps        ; make sure no traps are left set
  1176.     call    c_Unlock        ; and that the keyboard is unlocked
  1177.     cmp    pan_state,PS_LOADED    ; got a program loaded and ready to go?
  1178.     je    .cq2            ; if so we must get rid of it
  1179.     cmp    pan_state,PS_RUNNING    ; running a child program?
  1180.     je    .cq1            ; if so
  1181.     jmp    terminate        ; otherwise we can exit gracefully
  1182.  
  1183. .cq1:    mov    pan_state,PS_QUIT    ; quit when target program quits
  1184.     mov    ax,1
  1185.     ret
  1186.  
  1187. .cq2:    mov    child_cs,cs        ; fix things so child will die at birth
  1188.     mov    ax,OFFSET terminate
  1189.     mov    child_ip,ax
  1190.     mov    pan_state,PS_QUIT    ; quit when target program quits
  1191.     jmp    run_it            ; then go run it
  1192. c_Quit    endp
  1193.  
  1194. ;    RightShift ON/OFF
  1195.  
  1196. c_RightShift    proc    near
  1197.     mov    dl,01h            ; DL = Right-Shift bit in keyboard flags
  1198.     jmp    ShiftLock
  1199. c_RightShift    endp
  1200.  
  1201. ;    Screen <row> <column>  "string" - write a string directly onto the
  1202. ;                      screen.
  1203.  
  1204. c_Screen    proc    near
  1205.     call    get_screen_position    ; decode row and column
  1206.     call    skip_whitespace        ; skip to "string"
  1207.     call    normalize        ; copy and fix the string
  1208.     mov    dx,screen_position    ; DX = row + column
  1209.     mov    bl,va            ; BL = video attribute
  1210.     call    display_string        ; display the string
  1211.     ret
  1212. c_Screen    endp
  1213.  
  1214. ;    ScrollLock ON/OFF
  1215.  
  1216. c_ScrollLock    proc    near
  1217.     mov    dl,10h            ; DL = Scroll Lock bit in keyboard flags
  1218.     jmp    ShiftLock
  1219. c_ScrollLock    endp
  1220.  
  1221. ;    SetIf - set the if nesting level after a Label.
  1222. ;
  1223. ; Note:  This is an internal command that is inserted automatically
  1224. ;        following each Label command.  The effect is to make Ifs and
  1225. ;        EndIfs work like proper bracket operators.  It allows Jumps
  1226. ;        to be made out of If/EndIf blocks.  It also, er, allows Jumps
  1227. ;        to be made into If/EndIf blocks!!
  1228.  
  1229. c_SetIf    proc    near
  1230.     lodsb                ; AL = current level
  1231.     mov    if_nest_level,al    ; make that the nesting level
  1232.     mov    if_effect_level,al    ; and the effective level
  1233.     ret
  1234. c_SetIf    endp
  1235.  
  1236. ;    SetMemory <n> - sets the largest block of free memory to <n>KB
  1237.  
  1238. c_SetMemory    proc    near
  1239.     cmp    pan_state,PS_INITIAL    ; only useable in initial state
  1240.     jne    .sm7
  1241.     xor    ax,ax
  1242.     xchg    ax,allocated_block    ; release any previous allocation
  1243.     test    ax,ax
  1244.     jz    .sm1            ; if none
  1245.     push    es
  1246.     mov    es,ax
  1247.     mov    ah,49h
  1248.     int    21h            ; assume success
  1249.     pop    es
  1250.  
  1251. .sm1:    call    decode_decimal        ; get AX = decimal KB parameter
  1252.     mov    cl,6            ; convert to paragraphs
  1253.     shl    ax,cl
  1254.     push    ax
  1255.     mov    ah,48h            ; determine size of largest free
  1256.     mov    bx,-1            ;   memory block
  1257.     int    21h
  1258.     jnc    .sm8            ; should fail, begorrah!
  1259.     pop    ax
  1260.     sub    bx,ax            ; is there enough memory?
  1261.     js    .sm9            ; no
  1262.     mov    ah,48h            ; allocate the difference
  1263.     int    21h
  1264.     jc    .sm8            ; should not fail!
  1265.     mov    allocated_block,ax
  1266.     ret
  1267.  
  1268. .sm7:    mov    si,OFFSET .smm1        ; complain, complain, complain
  1269.     jmp    command_error
  1270.  
  1271. .sm8:    mov    si,OFFSET .smm2        ; bitch, bitch, bitch
  1272.     jmp    command_error
  1273.  
  1274. .sm9:    mov    si,OFFSET .smm3        ; moan, moan, moan
  1275.     jmp    command_error
  1276.  
  1277. .smm1    db    'May be used only before a program is loaded',0
  1278. .smm2    db    'Memory allocation error',0
  1279. .smm3    db    'Insufficient memory available',0
  1280. c_SetMemory    endp
  1281.  
  1282. ;    Tone <frequency> <duration> - sound a tone on the PC speaker
  1283. ;
  1284. ; Note:  The following procedure is based on code originally developed
  1285. ; by Joe Campbell.  The original is Copyright 1985, 1986 by Joe
  1286. ; Campbell, Box 7159, Berkeley, CA 94707.
  1287.  
  1288. LOWEST_HZ    EQU    50
  1289. HIGHEST_HZ    EQU    15000
  1290.  
  1291. c_Tone    proc    near
  1292.     call    decode_decimal        ; get AX = frequency
  1293.     test    ax,ax
  1294.     jz    .ton1            ; 0 => cancel previous tone
  1295.     cmp    ax,LOWEST_HZ        ; check frequency okay
  1296.     jb    .ton3
  1297.     cmp    ax,HIGHEST_HZ
  1298.     ja    .ton3
  1299.     mov    di,ax            ; DI = frequency
  1300.     mov    al,0B6h            ; set timer mode to oscillator
  1301.     out    43h,al
  1302.     mov    dx,12h            ; 1.193180 MHz = 1234DC hex
  1303.     mov    ax,34DCh
  1304.     div    di            ; divide DX:AX by DI, result to AX
  1305.     out    42h,al            ; write low byte of count
  1306.     xchg    al,ah            ; AL = high byte
  1307.     out    42h,al            ; write high byte
  1308.     in    al,61h            ; get current speaker port setting
  1309.     mov    .tonp,al        ; save speaker-port setting
  1310.     or    al,3            ; turn speaker on
  1311.     out    61h,al
  1312.     call    skip_whitespace
  1313.     call    decode_duration        ; decode duration, get AX = ticks
  1314.     test    ax,ax
  1315.     jz    .ton2
  1316.     mov    time_out,ax
  1317.     mov    recall_address,OFFSET .ton1
  1318.     ret
  1319.  
  1320. .ton1:    mov    recall_address,0    ; cancel recall
  1321.     mov    al,.tonp        ; otherwise, recover value of port
  1322.     and    al,NOT 03h        ; reset speaker-on bit
  1323.     out    61h,al
  1324.  
  1325. .ton2:    ret
  1326.  
  1327. .ton3:    mov    si,OFFSET .tonm1
  1328.     jmp    command_error
  1329.  
  1330. .tonm1    db    "Frequency outside valid range of 50 - 15,000 Hz",0
  1331.  
  1332. .tonp    db    0            ; saved setting of speaker port
  1333. c_Tone    endp
  1334.  
  1335. ;    TypeRate <ticks> - set a rate for emulating typing (in ticks)
  1336.  
  1337. c_TypeRate    proc    near
  1338.     call    decode_decimal        ; decode decimal tick count
  1339.     mov    type_rate,ax        ; store the type rate
  1340.     ret
  1341. c_TypeRate    endp
  1342.  
  1343. ;    Unlock - connect keyboard to application
  1344.  
  1345. c_Unlock    proc    near
  1346.     cmp    keyboard_state,1    ; is keyboard locked?
  1347.     jne    .unl1            ; if not this is a no-op
  1348.     dec    keyboard_state        ; set state to unlocked
  1349.     mov    al,9h            ; remove keyboard intercept
  1350.     mov    bx,OFFSET i_keyboard
  1351.     call    restore_vector
  1352.     mov    al,23h            ; reset Control-Break vector
  1353.     mov    bx,OFFSET i_ctrl_break
  1354.     call    restore_vector
  1355.  
  1356. .unl1:    ret
  1357. c_Unlock    endp
  1358.  
  1359. ;    Video <attribute> - set video attribute.
  1360.  
  1361. c_Video    proc    near
  1362.     call    decode_hex        ; decode attribute into AL
  1363.     mov    va,al            ; and store it away
  1364.     ret
  1365. c_Video    endp
  1366.  
  1367. ;    WaitChild - wait for child to die.
  1368.  
  1369. c_WaitChild    proc    near
  1370.     cmp    pan_state,PS_RUNNING    ; only valid in running state
  1371.     jne    .wc3            ; error in any other state
  1372.     mov    pan_state,PS_OBIT    ; change state to PS_OBIT
  1373.     call    unset_traps        ; no longer need these
  1374.     inc    time_out        ; to stop command processing for now
  1375.     ret
  1376.  
  1377. .wc3:    mov    si,OFFSET .wcA        ; what is Pandora expected to do?
  1378.     jmp    command_error
  1379.  
  1380. .wcA    db    'No program running to wait for',0
  1381. c_WaitChild    endp
  1382.  
  1383. ;    WaitCursor <row> <column> - wait for the cursor to be positioned
  1384.  
  1385. c_WaitCursor    proc    near
  1386.     call    get_screen_position    ; decode row and column
  1387.     inc    time_out        ; check on next tick
  1388.     mov    recall_address,OFFSET .wcu1    ; at label .wcu1
  1389.     ret
  1390.  
  1391. .wcu1:    mov    ah,03h            ; BIOS Get Cursor Position
  1392.     xor    bx,bx            ; page 0
  1393.     int    10h            ; returns DH = row, DL = column
  1394.     cmp    dx,screen_position
  1395.     je    .wcu4            ; if the cursor is there
  1396.  
  1397. .wcu3:    mov    time_out,3        ; try again in 3 more ticks' time
  1398.     ret
  1399.  
  1400. .wcu4:    mov    recall_address,0
  1401.     ret
  1402. c_WaitCursor    endp
  1403.  
  1404. ;    WaitScreen <row> <column> "string" - wait for the given string to
  1405. ;                         appear on screen.
  1406.  
  1407. c_WaitScreen    proc    near
  1408.     call    get_screen_position    ; decode row and column
  1409.     call    skip_whitespace        ; skip to the "string"
  1410.     call    normalize        ; copy and normalize the string
  1411.     inc    time_out        ; check on next tick
  1412.     mov    recall_address,OFFSET .ws1    ; at label .ws1
  1413.     ret
  1414.  
  1415. .ws1:    mov    si,OFFSET line_buffer    ; SI -> string to be matched
  1416.     call    check_screen        ; see if it's there
  1417.     jnc    .ws4            ; if the string has appeared
  1418.  
  1419. .ws3:    mov    time_out,3        ; try again in 3 more ticks' time
  1420.     ret
  1421.  
  1422. .ws4:    mov    recall_address,0
  1423.     ret
  1424. c_WaitScreen    endp
  1425.  
  1426. ;    WaitUntil <HH:MM> - wait until a given time of day
  1427.  
  1428. c_WaitUntil    proc    near
  1429.     cmp    pan_state,PS_RUNNING    ; cannot do this in background mode
  1430.     jae    .wu5
  1431.     call    decode_time        ; decode "HH:MM"
  1432.     mov    time_out,18        ; check every second
  1433.     mov    recall_address,OFFSET .wu1    ; below
  1434.     ret
  1435.  
  1436. .wu1:    mov    ah,2Ch            ; DOS Get Time
  1437.     int    21h
  1438.     cmp    cx,time_argument    ; has the due time come around?
  1439.     jne    .wu3            ; no, keep waiting
  1440.  
  1441. .wu2:    mov    recall_address,0    ; yes, do next command
  1442.     ret
  1443.  
  1444. .wu3:    mov    ah,01h            ; check for keyboard input
  1445.     pushf
  1446.     call    [i_BIOS_kb]        ; int    16h
  1447.     jz    .wu4            ; if none
  1448.     xor    ax,ax            ; read that input
  1449.     pushf
  1450.     call    [i_BIOS_kb]        ; int    16h
  1451.     cmp    al,1Bh            ; Escape?
  1452.     jne    .wu4            ; ignore anything but
  1453.     cmp    pan_state,PS_RUNNING    ; running a program?
  1454.     je    .wu2            ; yes, skip to next command
  1455.     jmp    terminate        ; no, terminate the program
  1456.  
  1457. .wu4:    mov    time_out,18        ; wait another second
  1458.     ret
  1459.  
  1460. .wu5:    mov    si,OFFSET .wuA        ; "Command not valid during background operation"
  1461.     jmp    command_error
  1462.  
  1463. .wuA    db    'Command not valid during background operation',0
  1464. c_WaitUntil    endp
  1465.  
  1466. ;    Wipe - clear the screen
  1467.  
  1468. c_Wipe    proc    near
  1469.     mov    ax,0600h        ; BIOS Initialize Window
  1470.     mov    bh,07h            ; use "white on black" attribute
  1471.     xor    cx,cx
  1472.     mov    dl,screen_columns
  1473.     mov    dh,44            ; assume largest text screen
  1474.     int    10h            ; returns AL = display mode
  1475.     ret
  1476. c_Wipe    endp
  1477.  
  1478. ; Procedures for handling commands while command-processing is inhibited.
  1479.  
  1480. ;    Process any kind of IF command when processing suspended
  1481.  
  1482. n_If    proc    near
  1483.     inc    if_nest_level        ; one level of If/EndIf deeper
  1484.     ret
  1485. n_If    endp
  1486.  
  1487. ; Regular commands are no-ops
  1488.  
  1489. n_Nop    proc    near
  1490.     ret
  1491. n_Nop    endp
  1492.  
  1493.  
  1494. ;******************************************************************************
  1495. ;*                                                                            *
  1496. ;*                        Miscellaneous procedures                            *
  1497. ;*                                                                            *
  1498. ;******************************************************************************
  1499.  
  1500. ;    check_screen - checks if a given string appears at a given screen position
  1501. ;
  1502. ; Called with:
  1503. ;    SI -> string to be sought
  1504. ;    'screen_position' holding the row and column
  1505. ;
  1506. ; Returns:
  1507. ;    CF = 0 if string is found
  1508. ;     CF = 1 otherwise
  1509.  
  1510. check_screen    proc    near
  1511.     push    es
  1512.     mov    dx,screen_position    ; set Pandora screen position
  1513.     call    set_video_address
  1514.  
  1515. .chs1:    cmp    BYTE PTR [si],0        ; check the next byte
  1516.     je    .chs3            ; if null we matched the whole string!
  1517.     mov    ax,es:[di]        ; AH = attribute, AL = character code
  1518.     cmp    [si],al            ; is character the one we want?
  1519.     jne    .chs2            ; no, so match fails...
  1520.     inc    di            ; yes, check next
  1521.     inc    di
  1522.     inc    si
  1523.     jmp    SHORT .chs1
  1524.  
  1525. .chs2:    stc                ; return CF set for failure
  1526.  
  1527. .chs3:    pop    es
  1528.     ret        ; returns CF = 0 if match else CF = 1
  1529. check_screen    endp
  1530.  
  1531. ;    command_error - spits out error information and quits.
  1532. ;
  1533. ; Called with:
  1534. ;    SI -> diagnostic (null terminated string)
  1535.  
  1536. command_error    proc    near    ; SI -> diagnostic message
  1537.     push    si            ; save diagnostic pointer
  1538.     cmp    pan_state,PS_LOADED    ; check the state
  1539.     jbe    .ce1            ; if Pandora is in control
  1540.     cli                ; else turn off interrupts
  1541.  
  1542. ; Prepare screen for messages
  1543.  
  1544. .ce1:    mov    al,init_video_mode    ; revert to original video mode
  1545.     mov    ah,00h            ; BIOS set video mode
  1546.     int    10h            ; which incidentally clears the screen
  1547.  
  1548.     mov    si,OFFSET ferrmsg    ; "Fatal error in Pandora Command:  "
  1549.     call    ttyz
  1550.     call    reconstruct_command    ; recreate text of command
  1551.     call    ttyz            ; and display it
  1552.     mov    si,OFFSET crlfz
  1553.     call    ttyz
  1554.     pop    si            ; display the specific diagnostic
  1555.     call    ttyz
  1556.     cmp    pan_state,PS_LOADED    ; check the state
  1557.     ja    .ce2            ; if Pandora is not in control
  1558.     jmp    c_Quit            ; then get out quick
  1559.  
  1560. .ce2:    mov    si,OFFSET bomb_msg2    ; else wait for confirmation
  1561.     call    ttyz
  1562.     xor    ax,ax            ; wait for input
  1563.     pushf
  1564.     call    [i_BIOS_kb]        ; int    16h
  1565.     xor    ax,ax            ; do a warm boot
  1566.     mov    ds,ax
  1567.     mov    ax,1234h
  1568.     mov    ds:[472h],ax
  1569.     db    0EAh            ; JMP FFFF:0000
  1570.     dw    0000h, 0FFFFh
  1571.  
  1572. bomb_msg2    db    CR,LF,'Press the [Space Bar] to reboot.',0
  1573. ferrmsg        db    'Fatal error in Pandora command:',CR,LF,0
  1574. command_error    endp
  1575.  
  1576. ;    compare_strings
  1577. ;
  1578. ; Called with:
  1579. ;    SI and DI -> strings to be compared
  1580. ;    CX = length
  1581. ;
  1582. ; Returns:
  1583. ;    CX, SI and DI unchanged
  1584. ;    flags:  see CMPS instruction
  1585.     
  1586. compare_strings    proc    near
  1587.     push    si
  1588.     push    di
  1589.     push    cx
  1590.     repe    cmpsb
  1591.     pop    cx
  1592.     pop    di
  1593.     pop    si
  1594.     ret
  1595. compare_strings    endp
  1596.  
  1597. ;    Copy a null-terminated string.
  1598. ;
  1599. ; Called with:
  1600. ;    SI -> source string (null terminated)
  1601. ;    DI -> destination
  1602. ;
  1603. ; Returns:
  1604. ;    SI = garbage
  1605. ;    DI -> null at end of the copy
  1606.  
  1607. copyz    proc    near
  1608.  
  1609. .cz1:    lodsb                ; copy each byte including the null
  1610.     stosb
  1611.     test    al,al
  1612.     jnz    .cz1            ; continue until null
  1613.     dec    di            ; DI -> null at end of copied string
  1614.     ret
  1615. copyz    endp
  1616.  
  1617. ;    Copy a delimited terminated string to 'line_buffer'.
  1618. ;
  1619. ; Called with:
  1620. ;    SI -> "string"
  1621. ;
  1622. ; Returns:
  1623. ;    SI -> copy
  1624. ;    DI = garbage
  1625.  
  1626. copy_quoted_string    proc    near
  1627.     mov    di,OFFSET line_buffer    ; DI -> standard destination
  1628.     push    di
  1629.     lodsb                ; AL = delimiter
  1630.     mov    ah,al            ; keep in AH
  1631.  
  1632. .cqs1:    lodsb                ; AL = next character
  1633.     test    al,al            ; allow missing closing delimiter
  1634.     jz    .cqs3            ; if end of string
  1635.     cmp    al,ah            ; delimiter?
  1636.     jz    .cqs3
  1637.     stosb
  1638.     jmp    SHORT .cqs1
  1639.  
  1640. .cqs3:    xor    ax,ax            ; store null terminator
  1641.     stosb
  1642.     pop    si            ; SI -> line_buffer
  1643.     ret        ; returns SI -> copied string
  1644. copy_quoted_string endp
  1645.  
  1646. ;    copy a null-terminated string
  1647. ;
  1648. ; Called with:
  1649. ;    DS:SI -> source
  1650. ;    ES:DI -> destination
  1651. ;
  1652. ; Returns:
  1653. ;    DS:SI -> null terminator of source
  1654. ;    ES:DI -> null terminator of copy
  1655.  
  1656. copy_string    proc    near
  1657.  
  1658. .cs1:    lodsb
  1659.     stosb
  1660.     test    al,al
  1661.     jnz    .cs1
  1662.     dec    si
  1663.     dec    di
  1664.     ret
  1665. copy_string    endp
  1666.  
  1667. ;    decode a decimal number
  1668. ;
  1669. ; Called with:
  1670. ;    SI -> numeric string
  1671. ;
  1672. ; Returns:
  1673. ;    SI -> first non-numeric character in string
  1674. ;    AX = decoded value
  1675.  
  1676. decode_decimal    proc    near
  1677.     xor    bx,bx            ; decode value into BX
  1678.     xor    cx,cx            ; keep sign indication in CH
  1679.     cmp    BYTE PTR [si],'+'    ; initial + or - is allowed
  1680.     je    .dec0
  1681.     cmp    BYTE PTR [si],'-'
  1682.     jne    .dec1
  1683.     inc    ch
  1684.  
  1685. .dec0:    inc    si            ; push SI past sign
  1686.  
  1687. .dec1:    xor    ax,ax
  1688.     lodsb                ; AX = next character
  1689.     sub    al,'0'            ; check if it's a digit
  1690.     jl    .dec2
  1691.     cmp    al,9
  1692.     jg    .dec2
  1693.     xchg    ax,bx            ; AX = cumulative total
  1694.     mul    ten            ; multiply by ten, ignore overflow into DX
  1695.     add    bx,ax            ; and add in the new digit
  1696.     jmp    .dec1
  1697.  
  1698. .dec2:    mov    ax,bx            ; AX = decoded value for return
  1699.     test    ch,ch            ; + or -
  1700.     jz    .dec3            ; if +
  1701.     neg    ax            ; if - then negate it
  1702.  
  1703. .dec3:    dec    si            ; back up SI to first non-digit
  1704.     ret
  1705.  
  1706. ten    dw    10
  1707. decode_decimal    endp
  1708.  
  1709. ;    Decode a duration (ticks/seconds/minutes)
  1710. ;
  1711. ; Called with:
  1712. ;    SI -> encoded duration
  1713. ;
  1714. ; Returns:
  1715. ;    If decoded successfully,
  1716. ;        CF = 0, and
  1717. ;        AX = value decoded into ticks
  1718. ;    If error then
  1719. ;        CF = 1
  1720.  
  1721. decode_duration    proc    near
  1722.     call    decode_decimal        ; decode decimal count
  1723.     test    ah,ah            ; 0 - 255 allowed
  1724.     jnz    .dur2            ; if out of bounds
  1725.     mov    cx,ax            ; CX = number
  1726.     call    skip_whitespace        ; skip to units
  1727.     jz    .durs            ; if no units then use seconds
  1728.     call    isletter        ; check that units starts with a letter
  1729.     jc    .dur2            ; give error if it doesn't
  1730.     cmp    al,'T'            ; ticks?
  1731.     je    .durt
  1732.     cmp    al,'S'            ; seconds?
  1733.     je    .durs
  1734.     cmp    al,'M'            ; minutes?
  1735.     jne    .dur2
  1736.     cmp    cl,60            ; 60 minutes is the max
  1737.     jg    .dur2
  1738.     mov    ax,1092            ; AX = number of ticks in a minute
  1739.     mul    cx            ; get AX = number of seconds
  1740.     jmp    SHORT .dur0
  1741.  
  1742. .durs:    mov    al,18            ; multiple by 18.25 to approximate 18.2
  1743.     mul    cl
  1744.     shr    cx,1
  1745.     shr    cx,1
  1746.     add    ax,cx
  1747.     jmp    SHORT .dur0
  1748.  
  1749. .durt:    mov    ax,cx            ; AX = tick count
  1750.  
  1751. .dur0:    clc
  1752.     ret
  1753.  
  1754. .dur2:    stc
  1755.     ret
  1756. decode_duration    endp
  1757.  
  1758. ;    decode a hex number
  1759. ;
  1760. ; Called with:
  1761. ;    SI -> numeric string
  1762. ;
  1763. ; Returns:
  1764. ;    SI -> first non-numeric character in string
  1765. ;    AX = decoded value
  1766.  
  1767. decode_hex    proc    near
  1768.     xor    bx,bx            ; put decoded value in bx
  1769.  
  1770. .hex1:    lodsb                ; AL = next character
  1771.     cmp    al,'0'            ; check if it's a hexit
  1772.     jl    .hex2
  1773.     cmp    al,'9'
  1774.     jg    .hex2
  1775.     sub    al,'0'
  1776.  
  1777. .hex0:    mov    cl,4            ; multiply result so far by 16
  1778.     shl    bx,cl
  1779.     add    bx,ax            ; and add in the new hexit
  1780.     jmp    .hex1
  1781.  
  1782. .hex2:    call    isletter
  1783.     jc    .hex3            ; if not a letter
  1784.     cmp    al,'G'
  1785.     jae    .hex3
  1786.     sub    al,'A'-10
  1787.     jmp    .hex0
  1788.  
  1789. .hex3:    mov    ax,bx            ; set result in AX
  1790.     dec    si            ; point SI to terminator
  1791.     ret
  1792. decode_hex    endp
  1793.  
  1794. ;    decode a time in the "HH:MM" format
  1795. ;
  1796. ; Called with:
  1797. ;    SI -> numeric string
  1798. ;
  1799. ; Returns:
  1800. ;    SI -> first non-numeric character in string
  1801. ;    AX = decoded value
  1802. ;
  1803. ; Side effects:
  1804. ;    Sets 'time_argument' to decoded value
  1805.  
  1806. decode_time    proc    near
  1807.     call    decode_decimal        ; decode hours
  1808.     mov    hour,al            ; save
  1809.     inc    si            ; puch pointer past ':'
  1810.     call    decode_decimal        ; decode minutes
  1811.     mov    minute,al        ; save that
  1812.     mov    ax,time_argument
  1813.     ret
  1814. decode_time    endp
  1815.  
  1816. ;    delay - pause for a given count of clock ticks.
  1817. ;
  1818. ; Called with:
  1819. ;    CX = number of 18.2-to-a-second ticks
  1820.  
  1821. CLOCK    =    46Ch        ; low-memory timer word
  1822. delay    proc    near
  1823.     push    es            ; get ES = 0
  1824.     xor    ax,ax
  1825.     mov    es,ax
  1826.     mov    ax,es:[CLOCK]        ; AX = current clock value
  1827.  
  1828. .del1:    cmp    ax,es:[CLOCK]        ; count down changes in the clock
  1829.     je    .del1
  1830.     mov    ax,es:[CLOCK]
  1831.     loop    .del1
  1832.  
  1833.     pop    es
  1834.     ret
  1835. delay    endp
  1836.  
  1837. ;    display_string    - display a null-terminated string on the screen.
  1838. ;
  1839. ; Called with:
  1840. ;    DX = screen position
  1841. ;    BL = video attribute
  1842. ;    SI -> string
  1843.  
  1844. display_string    proc    near    ; DX = screen position, BL = video attribute
  1845.     push    es
  1846.     call    set_video_address    ; get ES:DI -> video buffer
  1847.     mov    ah,bl            ; AH = attribute
  1848.  
  1849. .ds1:    lodsb                ; AL = next character from string
  1850.     test    al,al            ; ends at a null
  1851.     jz    .ds2
  1852.     stosw                ; pop into video memory
  1853.     jmp    SHORT .ds1
  1854.  
  1855. .ds2:    pop    es
  1856.     ret
  1857. display_string    endp
  1858.  
  1859. ; Find the Master Environment Block
  1860. ;
  1861. ; Returns:
  1862. ;    CF = 0 if global environment table found
  1863. ;        and AX = segment address of global environment table
  1864. ;    CF = 1 and AX = 0 otherwise
  1865.  
  1866. find_MEB    proc    near
  1867.     push    bx
  1868.     push    es
  1869.     xor    ax,ax
  1870.     mov    es,ax
  1871.     mov    ax,es:[0BAh]        ; get COMMAND.COM segment from 2E i.v.
  1872.     mov    es,ax            ; ES -> MCB for COMMAND.COM
  1873.     mov    ax,es:[2Ch]
  1874.     test    ax,ax
  1875.     stc
  1876.     jz    .meb1
  1877.     clc
  1878.  
  1879. .meb1:    pop    es
  1880.     pop    bx
  1881.     ret
  1882. find_MEB    endp
  1883.  
  1884. ;    get_keyfile - read a keycode from the KeyFile file
  1885. ;
  1886. ; Called with:
  1887. ;    (nothing)
  1888. ;
  1889. ; Returns:
  1890. ;    AX = keycode and CF = 0
  1891. ;    or CF = 1 if EOF
  1892.  
  1893. get_keyfile    proc    near
  1894.     mov    ah,3Fh
  1895.     mov    bx,keyfile_handle
  1896.     mov    cx,1
  1897.     mov    dx,OFFSET .gkpot
  1898.     int    21h
  1899.     jc    .gkf1
  1900.     mov    al,.gkpot        ; AL = ASCII code
  1901. ; translate to a keycode
  1902.     clc
  1903.  
  1904. .gkf1:    ret
  1905.  
  1906. .gkpot    db    0            ; for reading bytes into
  1907. get_keyfile    endp
  1908.  
  1909. ;    get_kiq - load a keycode from the keyboard input queue
  1910. ;
  1911. ; Called with:
  1912. ;    (nothing)
  1913. ;
  1914. ; Returns:
  1915. ;    AX = keycode and CF = 0
  1916. ;    or CF = 1 if kiq is exhausted
  1917.  
  1918. get_kiq    proc    near
  1919.     mov    si,kiq_first        ; SI -> string of key codes
  1920.     call    translate_key        ; translate next character
  1921.     mov    kiq_first,si        ; update pointer
  1922.     ret
  1923. get_kiq    endp
  1924.  
  1925. ;    get_screen_position - decode a row-column spec.  Note that the row and
  1926. ;                  column numbers are counted from zero, and are
  1927. ;                  deliberately not checked for validity.
  1928. ; Called with:
  1929. ;     SI -> "<row> <column>"
  1930. ;
  1931. ; Stores the result in 'screen_position'.
  1932.  
  1933. get_screen_position    proc    near
  1934.     call    decode_decimal        ; decode row number
  1935.     mov    n_row,al
  1936.     call    skip_whitespace        ; skip separator
  1937.     call    decode_decimal        ; decode column number
  1938.     mov    n_col,al
  1939.     ret
  1940. get_screen_position    endp
  1941.  
  1942. ;    get_script - determines the script-file name from the command-line
  1943. ;             argument, loads and preprocesses the file.
  1944. ;
  1945. ; On return:
  1946. ;    AX = number of bytes read
  1947.  
  1948. get_script    proc    near
  1949.     mov    si,80h            ; SI -> command line
  1950.     xor    ax,ax            ; first character holds the lebgth
  1951.     lodsb
  1952.     mov    bx,ax            ; AX = BX = character count
  1953.     mov    [si+bx],ah        ; replace terminator with null
  1954.     call    skip_whitespace        ; skip any spaces
  1955.     mov    dx,OFFSET .gsB        ; "ERROR:  No script file specified"
  1956.     jz    .gs6            ; if no filename given
  1957.     mov    dx,si            ; DX -> filename
  1958.     xor    ax,ax
  1959.  
  1960. .gs1:    lodsb                ; see if name includes an extension
  1961.     cmp    al,'.'            ; that is a period
  1962.     jne    .gs2
  1963.     mov    ah,al            ; note period in AH
  1964.  
  1965. .gs2:    cmp    al,' '            ; take any control character as the end
  1966.     ja    .gs1            ; this is chancy but...
  1967.  
  1968.     cmp    ah,'.'
  1969.     je    .gs3            ; if an extension was given
  1970.     mov    di,si            ; else append the default
  1971.     dec    di
  1972.     mov    si,OFFSET pan_extension
  1973.     mov    cx,5            ; which is 5 characters long with null
  1974.     rep    movsb
  1975.  
  1976. .gs3:    mov    ax,3D00h        ; open the command file
  1977.     int    21h
  1978.     mov    dx,OFFSET .gsC
  1979.     jc    .gs7            ; if open returned an error
  1980.     mov    file_handle,ax        ; else save the handle
  1981.     call    load_script        ; load the script from the file
  1982.     jc    .gs7            ; if there was something wrong with it
  1983.     mov    ah,3Eh            ; DOS close file
  1984.     mov    bx,file_handle
  1985.     int    21h
  1986.     cmp    if_nest_level,0
  1987.     jnz    .gs4            ; if Ifs and EndIfs don't match
  1988.     mov    ax,di            ; return size of script
  1989.     sub    ax,OFFSET script_buffer
  1990.     clc
  1991.     ret
  1992.  
  1993. .gs4:    mov    dx,OFFSET .gsD        ; complain
  1994.  
  1995. .gs6:    stc
  1996.  
  1997. .gs7:    ret
  1998.  
  1999. .gsB    db    'Pandora Error:  No script file specified$'
  2000. .gsC    db    'Pandora Error:  Cannot find script file$'
  2001. .gsD    db    "Pandora Error:  Unbalanced Ifs and EndIfs$"
  2002. get_script    endp
  2003.  
  2004. ;    is_digit - checks if character is an ASCII-coded digit
  2005. ;
  2006. ; Called with:
  2007. ;    AL = character
  2008. ;
  2009. ; Returns:
  2010. ;    CF = 0 if character is a digit ('0' - '9')
  2011. ;    CF = 1 otherwise
  2012.  
  2013. is_digit    proc    near
  2014.     cmp    al,'0'            ; is it a numeric ASCII code?
  2015.     jb    .id1
  2016.     cmp    al,'9'
  2017.     ja    .id1
  2018.     clc
  2019.     ret
  2020.  
  2021. .id1:    stc
  2022.     ret
  2023. is_digit    endp
  2024.  
  2025. ;    isletter - check and fold a letter
  2026. ;
  2027. ; Called with:
  2028. ;    al = ASCII code
  2029. ;
  2030. ; Returns:
  2031. ;    CF = 0 if AL contains a letter
  2032. ;            1 otherwise
  2033. ;    AL = ASCII code, folded to uppercase if letter
  2034.  
  2035. isletter    proc    near
  2036.     cmp    al,'A'
  2037.     jb    .let1
  2038.     cmp    al,'Z'
  2039.     jbe    .let2
  2040.     cmp    al,'a'
  2041.     jb    .let1
  2042.     cmp    al,'z'
  2043.     ja    .let1
  2044.  
  2045. .let2:    and    al,0DFh            ; fold
  2046.     ret
  2047.  
  2048. .let1:    stc
  2049.     ret
  2050. isletter    endp
  2051.  
  2052. ;     loader - attempt to load a target program given a filename.
  2053. ; Note that the child is loaded and given as its environment a copy of the
  2054. ; master environment.  This is so that 'DOS "set xxx=yyy"' commands can be
  2055. ; given in Pandora scripts to establish a suitable environment for the child.
  2056. ;
  2057. ; Called with:
  2058. ;    SI -> program filename
  2059.  
  2060. loader    proc    near
  2061.     call    normalize        ; copy filename and arguments
  2062.     cmp    env_inherit,0        ; which environment do we pass on?
  2063.     mov    ax,ds:[2Ch]        ; AX = segment of our environment
  2064.     je    .load2            ; if child to get Pandora's own
  2065.     call    find_MEB        ; else get AX -> master env table
  2066.  
  2067. .load2:    mov    env_seg,ax        ; set environment for child
  2068.  
  2069.     mov    ax,4B01h        ; DOS Load Program and Return function
  2070.     mov    bx,OFFSET parameter_block    ; BX -> parameter block
  2071.     mov    dx,si            ; DX -> filename
  2072.     int    21h            ; returns in child context
  2073.     jc    .load1            ;     unless load attempt failed
  2074.     mov    child_size,bx        ; save size of program
  2075.     mov    ah,51h            ; DOS get PSP address
  2076.     int    21h            ; returns BX = segment of PSP
  2077.     mov    child_psp,bx        ; save that
  2078.     mov    al,50h            ; DOS set PSP address
  2079.     mov    bx,cs            ; set process back to us
  2080.     int    21h
  2081.     mov    pan_state,PS_LOADED    ; set state to PS_LOADED
  2082.     clc
  2083.  
  2084. .load1:    ret
  2085. loader    endp
  2086.  
  2087. ;    load_script - loads the script from a given opened file.
  2088. ;
  2089. ; Called with:
  2090. ;     'file_handle' containing the handle of the file.
  2091. ;
  2092. ; Returns:
  2093. ;    CF = 0 if script was loaded succesfully
  2094. ;    CF = 1 if an error occurred
  2095.  
  2096. load_script    proc    near
  2097.     mov    di,OFFSET script_buffer
  2098.  
  2099. .ls1:    mov    bx,file_handle
  2100.     call    read_line        ; read one line = one command
  2101.     jc    .ls3            ; on EOF
  2102.     call    skip_whitespace        ; skip any initial blanks
  2103.     test    al,al            ; blank line?
  2104.     jz    .ls1            ; yes, ignore it
  2105.     cmp    al,'*'            ; comment line?
  2106.     je    .ls1            ; yes, ignore it
  2107.     mov    bx,OFFSET command_keys    ; identify the command
  2108.     call    match_key        ; returns AL = command index if valid
  2109.     jnz    .ls4            ; if it's invalid
  2110.     push    di            ; save pointer to start of command
  2111.     inc    di            ; reserve a byte for command length
  2112.     stosb                ; store command index
  2113.     call    skip_whitespace        ; skip any blanks after command
  2114.  
  2115. .ls2:    lodsb                ; copy the rest of the line
  2116.     stosb
  2117.     test    al,al            ; including the null terminator
  2118.     jnz    .ls2
  2119.     pop    bx            ; BX -> start of command
  2120.     mov    ax,di            ; AX -> end of command
  2121.     sub    ax,bx            ; AX = length of command
  2122.     mov    [bx],al            ; store that
  2123.  
  2124. ; Do If/EndIf checking
  2125.  
  2126.     xor    ax,ax
  2127.     inc    bx
  2128.     mov    al,[bx]            ; AX = command index
  2129.     push    ax
  2130.     mul    command_entry_size
  2131.     mov    bx,ax            ; BX = offset of command table entry
  2132.     xor    ax,ax            ; get AX = the command type
  2133.     mov    al,BYTE PTR [command_table+PC_TYPE+bx]
  2134.     mov    bx,ax
  2135.     pop    ax            ; call preprocessor with AX = index
  2136.     call    [preprocessing_table+bx]
  2137.     jnc    .ls1            ; if no error
  2138.     ret                ; else return with CF set
  2139.  
  2140. .ls3:    xor    ax,ax            ; zero-length command at end of script
  2141.     stosw
  2142.     ret
  2143.  
  2144. .ls4:    call    ttyz            ; display the offending line
  2145.     mov    dx,OFFSET .lsA        ; DX -> "Invalid command"
  2146.     stc
  2147.     ret
  2148.  
  2149. .lsA    db    CR,LF,'Pandora Error:  Invalid command.$'
  2150. load_script    endp
  2151.  
  2152. ; Procedures for preprocessing commands:
  2153.  
  2154. pp_regular    proc    near    ; for regular commands there is nothing to do
  2155.     cmp    al,LABEL_INDEX        ; unless this was a label command
  2156.     jne    .ppr1
  2157.     mov    al,3            ; store length for a SetIf
  2158.     stosb
  2159.     mov    al,SETIF_INDEX        ; insert a SetIf
  2160.     stosb
  2161.     mov    al,if_nest_level
  2162.     stosb
  2163.  
  2164. .ppr1:    clc
  2165.     ret
  2166. pp_regular    endp
  2167.  
  2168. pp_If    proc    near        ; for Ifs increment the nest level
  2169.     inc    if_nest_level
  2170.     clc
  2171.     ret
  2172. pp_If    endp
  2173.  
  2174. pp_Else    proc    near        ; for Else ensure it's in an If block
  2175.     cmp    if_nest_level,0
  2176.     jnz    .ppe1
  2177.     mov    dx,OFFSET .ppeA        ; complain about misplaced Else
  2178.     stc
  2179.  
  2180. .ppe1:    ret
  2181.  
  2182. .ppeA    db    "Pandora Error:  'Else' command not in If/EndIf clause$"
  2183. pp_Else    endp
  2184.  
  2185. pp_EndIf    proc    near    ; For EndIf decrement the nest level
  2186.     cmp    if_nest_level,0
  2187.     jnz    .ppf1
  2188.     mov    dx,OFFSET .ppfA        ; complain about dangling EndIf
  2189.     stc
  2190.     ret
  2191.  
  2192. .ppf1:    dec    if_nest_level
  2193.     clc
  2194.     ret
  2195.  
  2196. .ppfA    db    "Error:  EndIf found with no matching If$"
  2197. pp_EndIf    endp
  2198.  
  2199. ;     match_key - match a string to a set of keys.  The comparison is for
  2200. ;            letters only and is case insensitive.
  2201. ;
  2202. ; Called with:
  2203. ;     BX -> list of keys
  2204. ;    SI -> string to be matched
  2205. ;
  2206. ; Returns:
  2207. ;    If match made:  ZR = 1 and AX = index of the key
  2208. ;    Else:  ZR = 0
  2209.  
  2210. match_key    proc    near
  2211.     push    di
  2212.     call    skip_whitespace        ; skip any leading blanks
  2213.     mov    di,si            ; SI, DI -> first non-white char
  2214.     xor    cx,cx            ; count keys in CX
  2215.  
  2216. .mat1:    mov    si,di            ; SI -> target of match
  2217.  
  2218. .mat2:    mov    ah,[bx]            ; AH = character to compare against
  2219.     inc    bx            ; bump the pointer
  2220.     test    ah,ah            ; check for end of key
  2221.     jz    .mat4            ; we got a match
  2222.     lodsb                ; AL = next character of string
  2223.     cmp    al,' '            ; match up to blank or control char
  2224.     jbe    .mat3
  2225.     cmp    al,ah            ; do the real comparison
  2226.     je    .mat2            ; if they match then keep trying
  2227.     xor    al,20h            ; else switch case of string char
  2228.     cmp    al,ah            ; and compare that way
  2229.     je    .mat2
  2230.  
  2231. .mat3:    cmp    BYTE PTR [bx],0        ; push BX to end of current key
  2232.     pushf
  2233.     inc    bx
  2234.     popf
  2235.     jnz    .mat3
  2236.     inc    cx            ; increment key counter
  2237.     cmp    BYTE PTR [bx],0        ; have we tried all keys?
  2238.     jnz    .mat1            ; no, try next
  2239.  
  2240. .mat35:    mov    si,di            ; no match, return SI as it was
  2241.     inc    cx            ; just to ensure that ZR = 0
  2242.     pop    di
  2243.     ret        ; no match:  return ZR = 0, SI as on entry
  2244.  
  2245. .mat4:    lodsb                ; AL = next character of string
  2246.     cmp    al,' '            ; it should be blank or control char
  2247.     ja    .mat35
  2248.     dec    si
  2249.     xor    ax,ax            ; set ZR
  2250.     mov    ax,cx            ; AX = key number
  2251.     pop    di
  2252.     ret        ; match: return ZR = 1, AX = key number
  2253. match_key    endp        ;        and SI -> character past key
  2254.  
  2255.  
  2256. ;     normalize - normalize translates a string containing control characters
  2257. ;            in the form '^X' while copying it to line_buffer.
  2258. ;
  2259. ; Called with:
  2260. ;    SI -> delimited string
  2261. ;
  2262. ; Returns:
  2263. ;    SI -> normalized string in 'line_buffer'
  2264. ;    DI -> end of normalized string
  2265. ;    CX = length
  2266.  
  2267. normalize    proc    near
  2268.     mov    di,OFFSET line_buffer
  2269.     push    di
  2270.     xor    ax,ax
  2271.     lodsb                ; AL = delimiter
  2272.     or    ah,al            ; keep in AH
  2273.     jz    .nor3            ; if no argument
  2274.  
  2275. .nor1:    lodsb                ; AL = next character
  2276.     test    al,al
  2277.     jz    .nor3            ; if end of input
  2278.     cmp    al,ah            ; end of delimited string?
  2279.     je    .nor3
  2280.     cmp    al,' '
  2281.     jb    .nor1            ; ignore "real" control characters
  2282.     cmp    al,'^'
  2283.     jne    .nor2
  2284.     lodsb
  2285.     cmp    al,'^'            ; ^^ means ^
  2286.     je    .nor2
  2287.     and    al,1Fh            ; make a control
  2288.  
  2289. .nor2:    stosb                ; and store into string
  2290.     jmp    SHORT .nor1
  2291.  
  2292. .nor3:    xor    ax,ax            ; store null terminator
  2293.     mov    [di],al
  2294.     pop    si            ; SI -> line_buffer
  2295.     mov    cx,di            ; calculate new length
  2296.     sub    cx,si
  2297.     ret        ; returns SI -> normalized string, CX = length
  2298.             ;    DI -> end of normalized string
  2299. normalize endp
  2300.  
  2301. ;     read_line - read one line from a file into line_buffer.
  2302. ;
  2303. ; Called with:
  2304. ;    BX = file handle
  2305. ;
  2306. ; Returns:
  2307. ;    If data read then:  CF = 0, SI -> line, CX = length
  2308. ;    Else CF = 1 (implies end-of-file)
  2309.  
  2310. read_line    proc    near
  2311.     mov    si,OFFSET line_buffer    ; SI -> line_buffer
  2312.     mov    cx,1            ; read one byte at a time
  2313.  
  2314. .re1:    mov    ah,3Fh            ; DOS read function
  2315.     mov    dx,si            ; DS:DX -> buffer
  2316.     int    21H
  2317.     jc    .re5            ; if read error
  2318.     test    ax,ax
  2319.     jz    .re4            ; if EOF
  2320.     mov    al,[si]            ; AL = byte just read
  2321.     cmp    al,1Ah            ; check for Control-Z EOF
  2322.     je    .re4            ; be good to folks who use ancient editors
  2323.     cmp    al,' '            ; control character?
  2324.     jb    .re2            ; if so
  2325.     inc    si            ; else bump buffer pointer
  2326.     cmp    si,OFFSET line_buffer+127; and check for overflow
  2327.     jb    .re1            ; handle over-long lines ungracefully!
  2328.  
  2329. .re3:    xor    ax,ax            ; null terminate the line
  2330.     mov    [si],al
  2331.     mov    cx,si            ; calculate its length
  2332.     mov    si,OFFSET line_buffer    ; SI -> line_buffer
  2333.     sub    cx,si            ; CX = line length
  2334.     clc
  2335.     ret        ; return with CF zero and SI -> input, CX = length
  2336.  
  2337. .re2:    cmp    al,CR            ; check for CR
  2338.     jne    .re1            ; and discard other control characters
  2339.     jmp    SHORT .re3        ; end the line on CR
  2340.  
  2341. .re4:    cmp    si,OFFSET line_buffer    ; accept a last line with no CR
  2342.     jne    .re3
  2343.  
  2344. .re5:    stc
  2345.     ret        ; EOF or read error, return with CF set
  2346. read_line    endp
  2347.  
  2348. ;    reconstruct_command - reconstruct the text form of the current
  2349. ;                command.
  2350. ;
  2351. ; Returns:
  2352. ;    SI -> command key
  2353.  
  2354. reconstruct_command    proc    near
  2355.     mov    di,OFFSET line_buffer    ; reconstruction done here
  2356.     push    di            ; save a copy for later
  2357.     mov    si,current_command    ; SI -> internal form of command
  2358.     xor    ax,ax            ; get AX = command index
  2359.     lodsb
  2360.     mul    command_entry_size    ; calculate AX = offset of entry
  2361.     push    si
  2362.     mov    si,ax
  2363.     mov    si,WORD PTR [command_table+PC_KEY+si]
  2364.     call    copyz            ; copy null-terminated string
  2365.     mov    al,' '            ; put in a blank
  2366.     stosb
  2367.     pop    si
  2368.     call    copyz            ; and copy the arguments
  2369.     pop    si            ; return SI -> reconstructed text
  2370.     ret
  2371. reconstruct_command    endp
  2372.  
  2373. ;    resolve jumps - replace labels in Jump commands with offsets.
  2374.  
  2375. resolve_jumps    proc    near
  2376.  
  2377. .rj1:    mov    si,command_ptr        ; SI -> next command
  2378.     xor    ax,ax
  2379.     lodsb                ; AX = command length
  2380.     test    ax,ax
  2381.     jz    .rj4            ; at end of script
  2382.     add    command_ptr,ax        ; update the command pointer
  2383.     lodsb                ; AX = command index
  2384.     cmp    al,JUMP_INDEX        ; is it a jump?
  2385.     jne    .rj1
  2386.  
  2387.     mov    di,si            ; DI -> target label
  2388.     mov    si,OFFSET script_buffer    ; scan through script for label
  2389.     xor    cx,cx
  2390.  
  2391. .rj2:    add    si,cx            ; SI -> next command
  2392.     xor    ax,ax
  2393.     lodsb                ; AX = length of current command
  2394.     test    ax,ax
  2395.     jz    .rj3            ; at end of script
  2396.     sub    ax,2
  2397.     mov    cx,ax            ; CX = length - 2
  2398.     lodsb                ; AX = command index
  2399.     cmp    al,LABEL_INDEX        ; is it a label?
  2400.     jne    .rj2
  2401.     call    compare_strings
  2402.     jne    .rj2
  2403.     add    si,cx            ; SI -> next command
  2404.     mov    [di],si            ; overwrite label in jump
  2405.     jmp    SHORT .rj1
  2406.  
  2407. .rj3:    mov    si,di            ; SI -> label
  2408.     call    ttyz            ; display the offending line
  2409.     mov    dx,OFFSET .rjA        ; DX -> "ERROR:  Label not found."
  2410.     stc
  2411.  
  2412. .rj4:    ret
  2413.  
  2414. .rjA    db    CR,LF,'Pandora Error:  Label not found.$'
  2415. resolve_jumps    endp
  2416.  
  2417. ;    restore_vector - restores a value into an interrupt vector
  2418. ;
  2419. ; On entry:
  2420. ;    AL = vector number
  2421. ;    DS:BX = address at old vector is stored
  2422. ;
  2423. ; Destroys AX.
  2424.  
  2425. restore_vector    proc    near
  2426.     push    si
  2427.     push    es
  2428.     xor    ah,ah            ; calculate offset of vector
  2429.     shl    ax,1            ;     = number * 4
  2430.     shl    ax,1
  2431.     mov    si,ax            ; SI = offset of vector
  2432.     xor    ax,ax
  2433.     mov    es,ax            ; ES:SI -> vector
  2434.     pushf
  2435.     cli                ; interrupts off during switch
  2436.     mov    ax,[bx]            ; move in the saved value
  2437.     mov    es:[si],ax
  2438.     mov    ax,[bx+2]
  2439.     mov    es:[si+2],ax
  2440.     popf
  2441.     pop    es
  2442.     pop    si
  2443.     ret
  2444. restore_vector    endp
  2445.  
  2446. ;    run_it - transfer control to child program.
  2447.  
  2448. run_it    proc    near
  2449.     mov    ax,5000h        ; DOS set PSP address
  2450.     mov    bx,child_psp        ; BX = PSP of loaded program
  2451.     int    21h
  2452.  
  2453.     cli
  2454.     mov    pan_sp,sp        ; save own SP
  2455.     mov    ss,child_ss        ; set child's stack
  2456.     mov    sp,child_sp
  2457.     sti
  2458.  
  2459.     pop    ax            ; dump original drive valid flag
  2460.     mov    ax,cx            ; set real drive valid flag
  2461.     push    child_cs        ; set stack to "return" to child
  2462.     push    child_ip
  2463.  
  2464.     mov    es,child_psp
  2465.     mov    ds,child_psp        ; DS = ES = child PSP
  2466.  
  2467.     mov    WORD PTR es:[000AH],OFFSET child_return
  2468.  
  2469.     xor    bx,bx
  2470.     xor    dx,dx
  2471.     xor    bp,bp
  2472.     xor    si,si
  2473.     xor    di,di
  2474.     mov    cs:in_pan_flag,bl    ; clear in-Pandora flag
  2475.     retf        ; Note that we are in a NEAR procedure
  2476.  
  2477. child_return:        ; returns in Pandora context except DS = ???
  2478.             ; SP restored from Load operation not from Go
  2479.     mov    ax,cs            ; make sure DS and ES are set
  2480.     mov    ds,ax
  2481.     mov    es,ax
  2482.     mov    sp,pan_sp        ; restore SP saved just above
  2483.     call    unset_traps        ; should this be here ??? ***
  2484.     mov    al,pan_state        ; check state while resetting it
  2485.     cmp    al,PS_OBIT        ; waiting for this?
  2486.     je    .cr1            ; yes, continue
  2487.     mov    pan_state,PS_QUIT    ; set state so death can occur and
  2488.     jmp    c_Quit            ;    quit if chump did not wait for die
  2489.  
  2490. .cr1:    mov    pan_state,PS_INITIAL    ; revert to initial state
  2491.     mov    time_out,0        ; to let interpretation continue
  2492.     ret
  2493. run_it    endp
  2494.  
  2495. ;    search_env - searches the local environment for a string
  2496. ;
  2497. ; Called with:
  2498. ;    SI -> name
  2499. ;
  2500. ; Returns:
  2501. ;    If string found then
  2502. ;        CF = 0
  2503. ;        DS:SI -> string  (**** Note:  DS = environment segment ****)
  2504. ;    Else
  2505. ;        CF = 1
  2506.  
  2507. search_env    proc    near
  2508.     mov    es,ds:[2Ch]        ; get ES -> environment segment
  2509.     xor    di,di            ; ES:DI -> start of environment
  2510.     mov    bx,si            ; keep BX -> name
  2511.  
  2512. .se1:    mov    si,bx            ; SI -> name
  2513.  
  2514. .se2:    lodsb
  2515.     test    al,al
  2516.     jz    .se4            ; if end of name
  2517.     call    isletter        ; to fold to uppercase
  2518.     mov    ah,es:[di]
  2519.     inc    di
  2520.     xchg    al,ah
  2521.     call    isletter        ; fold other to uppercase
  2522.     cmp    al,ah
  2523.     je    .se2
  2524.  
  2525. ; Mismatch on current name.  Push DI to start of next name.
  2526.  
  2527. .se3:    dec    di
  2528.     xor    ax,ax
  2529.     mov    cx,-1
  2530.     repne scasb
  2531.     cmp    es:[di],al
  2532.     jne    .se1
  2533.     mov    ax,ds
  2534.     mov    es,ax
  2535.     stc
  2536.     ret
  2537.  
  2538. ; Matched a name.  Check that string comes next.
  2539.  
  2540. .se4:    cmp    BYTE PTR es:[di],'='
  2541.     jne    .se3
  2542.  
  2543. .se5:    inc    di
  2544.     cmp    BYTE PTR es:[di],' '
  2545.     je    .se5
  2546.     mov    si,di
  2547.     push    ds            ; exchange DS and ES
  2548.     push    es
  2549.     pop    ds
  2550.     pop    es
  2551.     clc
  2552.     ret
  2553. search_env    endp
  2554.  
  2555. ;    set_traps - capture the timer and BIOS-keyboard-function interrupts.
  2556.  
  2557. set_traps    proc    near
  2558.     mov    dx,OFFSET timer_intercept    ; replace timer interrupt
  2559.     mov    al,8h
  2560.     mov    bx,OFFSET i_timer
  2561.     call    set_vector
  2562.  
  2563.     mov    dx,OFFSET BIOS_kb_intercept    ; replace BIOS-kb interrupt
  2564.     mov    al,16h
  2565.     mov    bx,OFFSET i_BIOS_kb
  2566.     call    set_vector
  2567.     ret
  2568. set_traps    endp
  2569.  
  2570. ;    set_vector - copies the contents of an interrupt vector then stores
  2571. ;             a new value in the vector.
  2572. ;
  2573. ; On entry:
  2574. ;    AL = vector number
  2575. ;    DS:DX = new address for interrupt vector
  2576. ;    DS:BX = address at which to store old vector
  2577. ;
  2578. ; Destroys AX and BX.
  2579.  
  2580. set_vector    proc    near
  2581.     push    si
  2582.     push    es
  2583.     xor    ah,ah            ; calculate offset of vector
  2584.     shl    ax,1            ;     = number * 4
  2585.     shl    ax,1
  2586.     mov    si,ax            ; SI = offset of vector
  2587.     xor    ax,ax
  2588.     mov    es,ax            ; ES:SI -> vector
  2589.     pushf
  2590.     cli                ; interrupts off during switch
  2591.     mov    ax,es:[si]        ; move out the old
  2592.     mov    [bx],ax
  2593.     mov    ax,es:[si+2]
  2594.     mov    [bx+2],ax
  2595.     mov    es:[si],dx        ; move in the new
  2596.     mov    ax,ds
  2597.     mov    es:[si+2],ax
  2598.     popf
  2599.     pop    es
  2600.     pop    si
  2601.     ret
  2602. set_vector    endp
  2603.  
  2604. ;    set_video_address - set the video address corresponding to a given
  2605. ;                row and column.
  2606. ;
  2607. ; Called with:
  2608. ;    DX = screen position (DH = row, DL = column)
  2609. ;
  2610. ; Returns:
  2611. ;    ES:DI -> corresponding word in video buffer memory
  2612.  
  2613. set_video_address    proc    near    ; DX = screen position
  2614.     mov    ax,video_segment
  2615.     mov    es,ax
  2616.     xor    di,di            ; ES:DI -> start of video buffer
  2617.     mov    al,dh            ; DH = row number
  2618.     mul    screen_columns
  2619.     xor    dh,dh
  2620.     add    ax,dx
  2621.     add    di,ax
  2622.     add    di,ax
  2623.     ret        ; returns ES:DI -> word in video buffer
  2624. set_video_address    endp
  2625.  
  2626. ; Handle a CapsLock, NumLock or ScrollLock...
  2627. ;
  2628. ; Called with:
  2629. ;    DL = Lock bit in keyboard flags
  2630.  
  2631. ShiftLock    proc    near
  2632.     mov    bx,OFFSET on_off    ; BX -> "ON/OFF"
  2633.     call    match_key        ; check the argument
  2634.     jne    .sl1            ; if not "ON" nor "OFF"
  2635.     push    es
  2636.     xor    bx,bx
  2637.     mov    es,bx
  2638.     mov    dh,dl
  2639.     not    dh            ; start by setting Lock Off
  2640.     and    es:[417h],dh
  2641.     test    ax,ax            ; On or Off?
  2642.     jz    .sl2            ; if Off
  2643.     or    es:[417h],dl        ; else set to On
  2644.  
  2645. .sl2:    pop    es
  2646.     ret
  2647.  
  2648. .sl1:    mov    si,OFFSET .slmsg    ; "...should have argument On or Off"
  2649.     jmp    command_error
  2650.  
  2651. .slmsg    db    'Caps/Num/ScrollLock should have argument "On" or "Off"',0
  2652. ShiftLock    endp
  2653.  
  2654. ;    skip_whitespace - skip blanks and tabs in a string.
  2655. ;
  2656. ; Called with:
  2657. ;    SI -> string
  2658. ;
  2659. ; Returns:
  2660. ;    SI -> first character that is neither a blank nor a tab
  2661. ;    AL = that character
  2662.  
  2663. skip_whitespace    proc    near
  2664.  
  2665. .sw1:    lodsb
  2666.     cmp    al,' '
  2667.     je    .sw1
  2668.     cmp    al,09h            ; check for TAB
  2669.     je    .sw1
  2670.     dec    si
  2671.     test    al,al
  2672.     ret        ; returns SI -> first non-white char, AL = said char
  2673. skip_whitespace    endp    ;    and ZR = 1 if character is a null
  2674.  
  2675. ;    stuff_keys - stuff keycodes into the BIOS keyboard buffer.
  2676.  
  2677. stuff_keys    proc    near
  2678.     pushf                ; save interrupt flag
  2679.     push    es
  2680.     mov    es,kbb_segment        ; ES = keyboard-buffer segment
  2681.     cli                ; no interrupts while poking key buffer
  2682.  
  2683. .sk0:    mov    bx,es:[KBB_TAIL]    ; get tail
  2684.     mov    di,bx            ; and copy
  2685.     inc    bx            ; bump tail pointer
  2686.     inc    bx
  2687.     cmp    bx,es:[KBB_END]
  2688.     jne    .sk1
  2689.     mov    bx,es:[KBB_START]    ; if wrapped around
  2690.  
  2691. .sk1:    cmp    bx,es:[KBB_HEAD]    ; any room in buffer
  2692.     mov    ax,1            ; for timeout
  2693.     je    .sk3            ; if not...
  2694.  
  2695.     pop    es
  2696.     mov    ax,key_getter        ; AX -> proc to load a keycode
  2697.     call    ax            ; returns AX = keycode
  2698.     push    es
  2699.     jc    .sk4            ; if at end of string
  2700.     mov    es,kbb_segment        ; ES = keyboard-buffer segment
  2701.     stosw                ; store scan code and ASCII to KBB
  2702.     mov    es:[KBB_TAIL],bx    ; update tail
  2703.  
  2704.     mov    ax,type_rate        ; AX = inter-key delay (in ticks)
  2705.     test    ax,ax
  2706.     jz    .sk0            ; if zero just continue
  2707.  
  2708. .sk3:    mov    time_out,ax        ; set new timeout
  2709.     jmp    SHORT .sk5
  2710.  
  2711. .sk4:    mov    recall_address,0    ; stop recall
  2712.  
  2713. .sk5:    pop    es
  2714.     popf
  2715.     ret
  2716. stuff_keys    endp
  2717.  
  2718. ;    terminate - terminate the current program.
  2719.  
  2720. terminate    proc    near
  2721.     mov    ax,4C00h        ; DOS terminate a program
  2722.     int    21h
  2723. terminate    endp
  2724.  
  2725. ;    test_eks - test for extended keyboard services.
  2726. ;
  2727. ; This code is based on that published in PC Magazine of June 12, 1990.  Jeff
  2728. ; Prosise provides this code as part of his Tutor column on page 384.
  2729. ;
  2730. ; Called with:
  2731. ;    (nothing)
  2732. ;
  2733. ; Returns:
  2734. ;    AX = 0 if the BIOS appears to support extended keyboard services.
  2735. ;    AX non-zero if it does not...
  2736.  
  2737. test_eks    proc    near
  2738.     push    es
  2739.     mov    ax,40h            ; clear the keyboard buffer
  2740.     mov    es,ax
  2741.     mov    ax,es:[1Ah]
  2742.     mov    es:[1Ch],ax
  2743.     mov    ax,05FFh        ; try to insert a keycode
  2744.     mov    cx,0FFFFh        ;   into the buffer
  2745.     int    16h
  2746.     or    al,al            ; not supported if AL is returned NZ
  2747.     jnz    .eks1
  2748.     mov    ah,10h            ; read back the keycode
  2749.     int    16h
  2750.     xor    ax,0FFFFh        ; to see if it matches
  2751.  
  2752. .eks1:    ret
  2753. test_eks    endp
  2754.  
  2755. ;    translate_key - translates a character in keyboard format
  2756. ;
  2757. ; Called with:
  2758. ;    SI -> string of encoded key symbols
  2759. ;
  2760. ; Returns:
  2761. ;    CF = 0 if character available, and
  2762. ;        AX = key code suitable for insertion into BIOS keyboard buffer
  2763. ;        DL = shift status for character
  2764. ;    CF = 1 if end-of-string
  2765.  
  2766. translate_key    proc    near    ; SI -> key spec
  2767.     push    bx            ; save all registers but those
  2768.     push    cx            ;    used to return stuff
  2769.     push    di
  2770.  
  2771. .tra1:    xor    dx,dx            ; prepare DL to hold shift information
  2772.  
  2773. ; We start by checking for a caret which is usually a Ctrl-shift indicator
  2774.  
  2775. .tra2:    cmp    [si],BYTE PTR '^'    ; Ctrl-shifted character?
  2776.     jne    .tra3
  2777.     inc    si
  2778.     cmp    [si],BYTE PTR '^'    ; doubled?
  2779.     je    .tra8            ; send character
  2780.     or    dl,04h            ; set "Ctrl key is down" bit in status
  2781.  
  2782. .tra3:    cmp    [si],BYTE PTR '['    ; special-key delimiter?
  2783.     jne    .tra8
  2784.     inc    si            ; push pointer past \
  2785.     cmp    [si],BYTE PTR '['    ; doubled?
  2786.     je    .tra8            ; '[[' means '['
  2787.     mov    bx,si            ; save pointer to '['
  2788.  
  2789. .tra4:    lodsb                ; search for closing ']'
  2790.     test    al,al            ; or end of string
  2791.     jz    .tra6            ; if no closing ']'
  2792.     cmp    al,']'
  2793.     jne    .tra4
  2794.     dec    si
  2795.     mov    BYTE PTR [si],0        ; replace the ']' with a null
  2796.     mov    si,bx            ; SI -> keyname
  2797.     mov    bx,OFFSET shiftname_list; check the list of shift-key names
  2798.     call    match_key        ; look it up
  2799.     jne    .tra5            ; if no match
  2800.     inc    si            ; push SI past the null
  2801.     mov    bx,ax
  2802.     or    dl,[shiftbits+bx]    ; or bit for shift key into DL
  2803.     jmp    SHORT .tra2
  2804.  
  2805. .tra5:    mov    bx,OFFSET keyname_list    ; try other named keys
  2806.     call    match_key        ; look it up
  2807.     jne    .tra6            ; if no match
  2808.     inc    si            ; push SI past the null
  2809.     mov    bx,ax
  2810.     mov    ah,[key_scans+bx]    ; AH = scan code
  2811.     xor    al,al            ; AL = zero
  2812.     jmp    SHORT .tra9
  2813.  
  2814. .tra6:    mov    al,[si]            ; AL = character following '['
  2815.     call    is_digit        ; only valid thing now is a decimal
  2816.     jc    .tra8            ;   code of exactly three digits
  2817.     xor    ax,ax
  2818.     call    decode_decimal        ; decode the code
  2819.     cmp    BYTE PTR [si],']'
  2820.     stc
  2821.     jne    .tra12
  2822.     inc    si            ; push SI past ']'
  2823.     test    al,al
  2824.     jz    .tra1            ; zero is invalid
  2825.     test    ah,ah
  2826.     jz    .tra11            ; accept only codes between 1 and 127
  2827.  
  2828. .tra7:    jmp    .tra1            ; need a long jump here
  2829.  
  2830. .tra8:    xor    ax,ax            ; load and return literal ASCII
  2831.     lodsb
  2832.     test    al,al            ; test for end of string
  2833.     stc                ; at end we return with CF set
  2834.     jz    .tra12
  2835.     js    .tra11            ; if extended ASCII (no scan code)
  2836.     mov    bx,ax
  2837.     mov    ah,[scan+bx]        ; AH = scan code
  2838.     xor    bx,bx            ; check if we need to add a Shift
  2839.     mov    bl,ah
  2840.     add    bx,bx
  2841.     add    bx,OFFSET No_shift
  2842.     cmp    [bx],al
  2843.     je    .tra9            ; if char matches without a Shift
  2844.     or    dl,02h            ; assume a Left Shift
  2845.  
  2846. ; Convert ASCII and scan codes according to shifts
  2847.  
  2848. .tra9:    test    dl,08h            ; Alt takes precedence
  2849.     mov    bx,OFFSET Alt_shift
  2850.     jnz    .tra10
  2851.     test    dl,04h            ; Ctrl is next
  2852.     mov    bx,OFFSET Ctrl_shift
  2853.     jnz    .tra10
  2854.     test    dl,03h            ; Shift is lowest
  2855.     mov    bx,OFFSET Shift_shift
  2856.     jnz    .tra10
  2857.     mov    bx,OFFSET No_shift
  2858.  
  2859. .tra10:    xchg    al,ah            ; get scan code in AL
  2860.     xor    ah,ah
  2861.     add    ax,ax            ; convert to word index
  2862.     add    bx,ax            ; BX -> entry in shift table
  2863.     mov    ax,[bx]            ; load revised codes
  2864.     test    ax,ax            ; zero entry means key combination
  2865.     jz    .tra7            ;    generates nothing
  2866.  
  2867. .tra11:    clc                ; return character and CF = 0
  2868.  
  2869. .tra12:    pop    di
  2870.     pop    cx
  2871.     pop    bx
  2872.     ret
  2873. translate_key    endp
  2874.  
  2875. ;    ttyz - display a null-terminated string at the cursor using the BIOS.
  2876. ;
  2877. ; Called with:
  2878. ;    SI -> string
  2879.  
  2880. ttyz    proc    near
  2881.     xor    bx,bx            ; assume page 0
  2882.  
  2883. .tz1:    lodsb                ; do it one character at a time
  2884.     test    al,al
  2885.     jz    .tz2
  2886.     mov    ah,0Eh            ; using the BIOS
  2887.     int    10h
  2888.     jmp    SHORT .tz1
  2889.  
  2890. .tz2:    ret
  2891. ttyz    endp
  2892.  
  2893. ;    unset_traps - remove traps set by set_traps.
  2894.  
  2895. unset_traps    proc    near
  2896.     mov    ax,x_timer_offset    ; were traps set?
  2897.     or    ax,x_timer_segment
  2898.     jz    .uns1            ; skip if not
  2899.     mov    al,8h            ; remove timer intercept
  2900.     mov    bx,OFFSET i_timer
  2901.     call    restore_vector
  2902.     mov    al,16h            ; remove BIOS-keyboard intercept
  2903.     mov    bx,OFFSET i_BIOS_kb
  2904.     call    restore_vector
  2905.  
  2906. .uns1:    ret
  2907. unset_traps    endp
  2908.  
  2909. ; Interrupt stack
  2910.  
  2911.     dw    80h DUP (0)
  2912. interrupt_stack    LABEL    WORD        ; stack used within interrupts
  2913.  
  2914. ; Regular stack
  2915.  
  2916.     dw    80h DUP (0)
  2917. regular_stack    LABEL    WORD        ; stack for use within Pandora
  2918.  
  2919. script_buffer    db    0        ; script loaded starting here
  2920. code    ends
  2921.     end    start
  2922.